5

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
  • 8,327
  • 9
  • 48
  • 63
DᴀʀᴛʜVᴀᴅᴇʀ
  • 7,681
  • 17
  • 73
  • 127

1 Answers1

5

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
  • 8,327
  • 9
  • 48
  • 63
  • I can bring in the component but for some reason it will not take any color applied. Any suggestions for applying the color? – DᴀʀᴛʜVᴀᴅᴇʀ Apr 01 '21 at 13:28
  • @DᴀʀᴛʜVᴀᴅᴇʀ how do you pass a color? – sergdenisov Apr 01 '21 at 15:03
  • I've tried adding color:red and fill:red to :after but the svg still renders black – DᴀʀᴛʜVᴀᴅᴇʀ Apr 01 '21 at 15:39
  • @DᴀʀᴛʜVᴀᴅᴇʀ added the color prop to the code, check out, please. – sergdenisov Apr 01 '21 at 16:35
  • I'll take the opportunity to learn as I know nothing about the tool, Is it needed to use CSS2 `:after` (instead of CSS3 `::after`) in styled-components? – Leo Apr 01 '21 at 17:14
  • 1
    @Leo no, it's your choice. – sergdenisov Apr 01 '21 at 17:33
  • @sergdenisov the color prop is not working for me. I do not require it as an argument from an element but a hardcoded value of #1F5462. My code looks like `url(${reactSvgComponentToMarkupString(FaCircle, 'color: #1F5462')})`}; but it doesn't work and still takes black. Also i would like to control width and height. how do I include them as well? – Simeon Mark Fernandes Oct 26 '22 at 11:33
  • @SimeonMarkFernandes you need to write `url(${reactSvgComponentToMarkupString(FaCircle, { color: '#1F5462' })})`. You also can add `width` and `height` props similar to my `color` prop. – sergdenisov Oct 27 '22 at 10:08