react-fontawesome icon indicating copy to clipboard operation
react-fontawesome copied to clipboard

Bug: Hydration Error When Using title on FontAwesome Icons in React

Open fakkio opened this issue 11 months ago • 3 comments

Describe the bug I'm encountering a hydration error when adding a title attribute to a FontAwesome icon in a React application using Next.js 15.1.4 and React 19.0.0.

Steps to Reproduce:

  1. Use a FontAwesome icon in a Next.js 15.1.4 project with React 19.0.0.
  2. Add a title directly to the icon, like this:
    <FontAwesomeIcon icon={faCoffee} title="Coffee Icon" />
    
  3. A hydration error appears in the console, indicating: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:

Real application error Output (Diff Example):

   ...
                  <FontAwesomeIcon icon={{prefix:"fad", ...}} fixedWidth={true} className="cursor-hel..." ...>
                    <svg
+                     aria-labelledby="svg-inline--fa-title-mCCtcrJhBD07"
-                     aria-labelledby="svg-inline--fa-title-icBmyD9gSN0D"
                      data-prefix="fad"
                      data-icon="user"
                      className="svg-inline--fa fa-user fa-fw cursor-help DataTable_rowOtherLink__ILvwg"
                      role="img"
                      xmlns="http://www.w3.org/2000/svg"
                      viewBox="0 0 448 512"
                      style={{}}
                      ref={null}
                    >
                      <title
+                       id="svg-inline--fa-title-mCCtcrJhBD07"
-                       id="svg-inline--fa-title-icBmyD9gSN0D"
                        style={{}}
                      >
+                       {"Il Contraente e l'Assicurato coincidono"}
                      ...
                  ...

Temporary Solution:
I managed to fix the issue by wrapping the icon in a <span>:

<span title="Coffee Icon">
  <FontAwesomeIcon icon={faCoffee} />
</span>

Environment:

  • Library: @fortawesome/fontawesome-svg-core v6.4.2
  • Library: @fortawesome/react-fontawesome v0.2.0
  • Framework: Next.js v15.1.4
  • React Version: v19.0.0

Thanks!

Desktop (please complete the following information):

  • Browser Chrome
  • Version 132

fakkio avatar Jan 29 '25 16:01 fakkio

Maybe it's related to #550?

fakkio avatar Feb 06 '25 09:02 fakkio

As I mentioned the maskId solution in #550, there is also titleId in: https://github.com/FortAwesome/react-fontawesome/blob/432b921d69d382c54ad9495fa9cbdcea539de05f/src/components/FontAwesomeIcon.js#L34

so maybe you can try adding some unique titleId:

<FontAwesomeIcon titleId='titled-icon' title='Coffee Icon' icon={faCoffee} />

romanato73 avatar Feb 20 '25 22:02 romanato73

yes that's a good idea, that's how I solved it

  let titleId
  if (Array.isArray(props.icon)) {
    const [, iconName] = props.icon
    titleId = iconName
  } else if (typeof props.icon === 'object') {
    titleId = props.icon.iconName
  } else {
    titleId = props.icon
  }
    
return <Icon {...props} titleId={titleId} />

but there is a unique case when you have two icons with the same icon titleId on the same page. You'll get an accessibility error. I am trying to solve for that. My first attempt is to just append unique id to titleId like this without trynna make it a client component but this will cause a hydration error

return <Icon {...props} titleId={titleId + Math.random() * 100} /> // hydration error

I'll have to turn this into a client component instead

hassenhassen1 avatar May 19 '25 20:05 hassenhassen1

@hassenhassen1 if you use any kind of randomiser or unique id generator for the title id, then it is going to run into the hydration error because it will get randomised/generated once on the server and then again in the client, yielding a mismatch.

Ideally, you should instead try to use some other static unique identifier for the particular icon item rather than a generated one. It could be some string that specifically references the location of the icon or other context around what's different about this instance of this icon versus other instances of the same icon, rather than just the icon's name for example.

charles4221 avatar Jul 30 '25 01:07 charles4221

I was able to use React's useId hook and it worked perfectly. https://react.dev/reference/react/useId

I just set titleId equal to {useId()} and that did the trick.

Maybe FontAwesome can be updated to default titleId to the same and that would solve the issue I think.

markdemich-fl avatar Jul 30 '25 12:07 markdemich-fl

@markdemich-fl nice! Makes perfect sense to use that hook for this.

Maybe FontAwesome can be updated to default titleId to the same and that would solve the issue I think

The main issue there is that the default titleId generation is done inside fontawesome-svg-core so it can't use React-specific implementations like useId hook.

I'm working on a rewrite of this library right now and will see if I can implement something for this specific to the react-fontawesome icon component.

charles4221 avatar Jul 31 '25 00:07 charles4221

I was thinking it can be fixed in the react component around this section of code.

https://github.com/FortAwesome/react-fontawesome/blob/0a6f97d1a66271828f87a8ff705c4d7d87f87bfc/src/components/FontAwesomeIcon.js#L75

basically pass titleId as

titleId || useId()

This way if the user didn't pass in titleId, the react component would inject one that would work.

markdemich-fl avatar Jul 31 '25 12:07 markdemich-fl

FYI, this will be fixed in #581 when it gets released as 3.0.0

charles4221 avatar Aug 15 '25 06:08 charles4221