3

In styled-components, I'm trying to get a React Icon to render on hover by passing it through content but for some reason, my render on hover is [Object Object].

Component:

export const FooLink = styled(Link)`
  padding: 0.5rem 2rem;
  color: ${({ theme }) => theme.colors.white};
  text-align: center;
  margin: 0 auto;
  position: relative;
  font-size: ${({ theme }) => theme.fontSizes.p};
  letter-spacing: 0.15em;
  transition: ${({ theme }) => theme.animations.trans3};

  &:after {
    content: '${FaArrowRight}';
    /* content: '>'; */
    position: absolute;
    opacity: 0;
    right: -${({ theme }) => theme.spacings.small};
    transition: ${({ theme }) => theme.animations.trans3};
  }

  &:hover {
    padding-right: ${({ theme }) => theme.spacings.small};
    padding-left: ${({ theme }) => theme.spacings.xxSmall};
  }

  &:hover:after {
    opacity: 1;
    ${({ theme }) => theme.spacings.xSmall};
  }
`

Ive brought everything in with:

import styled from 'styled-components'
import { Link } from 'gatsby'
import { FaArrowRight } from 'react-icons/fa'

Other attempts on content

content: ${FaArrowRight};

But I found this won't work per:

That is because content value must be within quotes in CSS.

Realizing I might have spent too long in a CSS mental state I tried bringing in React:

import React from 'react'
import styled from 'styled-components'
import { Link } from 'gatsby'
import { FaArrowRight } from 'react-icons/fa'

And rendering:

content: '${(<FaArrowRight />)}';

when I try with template literals I get an error with missing semi-colons:

content: `'${<FaArrowRight />}'`;

All attempts render as [Object Object]

Research to see if this has been asked and I've read through:

In styled-components how can I render a React Icon in content?

sergdenisov
  • 7,838
  • 8
  • 43
  • 60
DᴀʀᴛʜVᴀᴅᴇʀ
  • 5,839
  • 15
  • 57
  • 101

1 Answers1

3

If you need to use styled-components (or any other CSS-in-JS library) with an icon from react-icons (or any other library that exports a React.Component which renders an <svg> element), I see the only one way: to transform a component to an url() with a markup string because only this way you can pass an image to content property in your case. For that transformation, you need: React.createElement(), ReactDOMServer.renderToStaticMarkup() and encodeURIComponent(). Also, you can use Base64 instead.

This one works (CodeSandbox):

import { createElement } from "react";
import { render } from "react-dom";
import { renderToStaticMarkup } from "react-dom/server";
import styled from "styled-components";
import { Link } from "gatsby";
import { FaArrowRight } from "react-icons/fa";

window.___loader = { enqueue: () => {}, hovering: () => {} };

const reactSvgComponentToMarkupString = (Component, props) =>
  `data:image/svg+xml,${encodeURIComponent(
    renderToStaticMarkup(createElement(Component, props))
  )}`;

const FooLink = styled(Link)`
  ${({ color }) => color && `color: ${color};`}
  &:after {
    content: ${({ color }) =>
      `url(${reactSvgComponentToMarkupString(FaArrowRight, {
        color
      })})`};
    position: absolute;
    opacity: 0;
  }

  &:hover:after {
    opacity: 1;
  }
`;

render(
  <>
    <div>
      <FooLink to="/test1" color="red">
        Link with arrow (red)
      </FooLink>
    </div>
    <div>
      <FooLink to="/test2">Link with arrow (default)</FooLink>
    </div>
  </>,
  document.getElementById("root")
);

Thanks to this Github Gist.

sergdenisov
  • 7,838
  • 8
  • 43
  • 60