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

[feature request] Remove library.add() with a better solution

Open waynevanson opened this issue 5 years ago β€’ 17 comments
trafficstars

Is your feature request related to a problem? Please describe.

  • Importing each separate icon is too declarative.
  • Annoying developer experience - just let us start!
  • Have I missed an icon?
  • Generates huge boilerplate if looking to modularize library.add() into each component that uses an icon.
  • Not part of the react paradigm.

Describe the solution you'd like

  • Internally lazy load icons. Many bundlers code split when they see a lazy import (inline require()). This means it'll add itself into your app only when you call it.
  • Possibility to override it on a particular component if the component dynamically chooses an icon. An example would be an icon picker.

Describe alternatives you've considered

An alternative is what already exists: use library.add() to add icons to the sheet. An understandable solution, but I have no idea what icons I may be using for my project. I just want to use them.

Additional context

Picture to show I'm not being dramatic:

waynevanson avatar Dec 15 '19 05:12 waynevanson

You can do this using react-fontawesome:

import { faSquare } from '@fortawesome/fontawesome-svg-solid-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

<FontAwesomeIcon icon={faSquare} />

Since you are loading icons one at a time, this is going to be part of a lazy loaded chunk. So, you can just completely ignore using library. I personally like selecting icons based on names:

<FontAwesome icon="square" />

GasimGasimzada avatar Jan 06 '20 20:01 GasimGasimzada

Since you are loading icons one at a time, this is going to be part of a lazy loaded chunk. So, you can just completely ignore using library. I personally like selecting icons based on names:

<FontAwesome icon="square" />

@GasimGasimzada When I do the above, wouldn't I have to add the icon to the library? It seemed clear to me that the above only works when adding a library, according to this documentation.

That's why this issue was created in the first place.

waynevanson avatar Jan 09 '20 23:01 waynevanson

Yes you do but you can also use imported Icon directly into FontAwesome.

GasimGasimzada avatar Jan 10 '20 08:01 GasimGasimzada

I have to agree with OP on this one. The developer experience of library.add is not great. I've used @material-ui/icons and their API is simply:

import { AccessAlarm } from '@material-ui/icons';

I don't understand why react-fontawesome can't be:

import { SquareIcon } from '@fortawesome/react-solid'; // (or similar)

Am I missing something? In the context of code-split JAMstack apps, this import scheme seems more beneficial for optimizing bundles per page too.

ricokahler avatar Jan 17 '20 21:01 ricokahler

@ricokahler like this?

import { faSquare } from '@fortawesome/fontawesome-svg-solid-icons';

robmadole avatar Jan 17 '20 21:01 robmadole

@robmadole I was thinking theFontAwesomeIcon component would be in there too I suppose

the implementation of the SquareIcon could be as simple as this:

// SquareIcon.js
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSquare } from '@fortawesome/fontawesome-svg-solid-icons';

function SquareIcon(props) {
  return <FontAwesomeIcon icon={faSquare} {...props} />;
}

export default SquareIcon;

Realistically this is small wish list item but I would love it πŸ˜…

ricokahler avatar Jan 17 '20 21:01 ricokahler

Oh gotcha! Yeah, we probably won't do that because it would introduce a build step and make the library HUGE.

robmadole avatar Jan 17 '20 21:01 robmadole

Yeah that makes sense and I don't disagree with your sentiment, but on the flip side, the DX would probably be better if you bit the bullet.

If you're worried about the source code becoming huge, you can probably just defer creating the icon component files until build (kind of like a JAMstack app actually lol). The footprint of the library in NPM would definitely still be giant but in general bundlers would be able to tree shake everything unused.

Startup times might be slower since tree shaking generally doesn't happen until the production build is created but material-ui solved this problem using a babel transform plugin while exposing direct imports.

So here's the full proposal:

  • create JAMstack-esque setup that creates the different icon files during build. this build should also output a giant es-modules index.js file that's tree shakable
  • create a new repo for each type of icon (@fortawesome/react-solid, @fortawesome/react-light, etc)
  • expose each file so that they can be directly imported like import SquareIcon from '@fortawesome/react-solid/SquareIcon'; so that babel plugin material-ui suggested will work.

What does this achieve?

This πŸ‘‡

import { SquareIcon, CoffeeIcon } from '@fortawesome/react-solid'

What's the impact?

Users are now using ONE less line of code for while still creating tree shakable builds.

Is it worth it?

Maybe! I believe this API is marginally better πŸ˜…and has nearly the same API footprint (i.e. the import is still one line) as library.add.


…but also I understand if you're not feeling it πŸ˜‘

ricokahler avatar Jan 17 '20 22:01 ricokahler

Well described and thanks for illustrating the solution so well. But I don't think this provides enough of an advantage to devote the effort into it. So yeah, not quite feeling it πŸ™ƒ

robmadole avatar Jan 20 '20 17:01 robmadole

That's completely fair. Thanks for considering it.

ricokahler avatar Jan 21 '20 00:01 ricokahler

Not really what I'd necessarily recommend long term because its a lot of wasted imports/bloat, but if you just want to be able to access all the icons without being annoyed while you're prototyping, something like this will work and register all the icons :)


import { library } from "@fortawesome/fontawesome-svg-core";
import * as icons from "@fortawesome/free-solid-svg-icons";
import { isObject, isNull } from "lodash";

library.add(
  ...Object.keys(icons)
    .filter(icon => isObject(icons[icon]) && !isNull(icons[icon]))
    .map(icon => icons[icon])
);

nicholascm avatar Feb 22 '20 00:02 nicholascm

I found it too cumbersome to declare individual icons I use separately from where I actually use the icon but importing all icons bloats the codebase, so I created a babel plugin macro which transforms code like

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { fas } from 'fontawesome.macro'

<FontAwesome icon={fas`square`} />

into

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSquare } from "@fortawesome/free-solid-svg-icons"

<FontAwesome icon={ faSquare } />

https://github.com/javascripter/fontawesome.macro

I don't know if fontawesome team wants to include a helper like this in their package though, as this feels like a magic :)

javascripter avatar Mar 13 '20 13:03 javascripter

That's pretty slick! It does feel like magic but if you are using Babel you are signing up for at least a small cup of magic with your lunch. Let me think on this. Thanks for sharing @javascripter

robmadole avatar Mar 13 '20 14:03 robmadole

@javascripter Please publish your package on npm.

thorn0 avatar May 28 '20 20:05 thorn0

We'll certainly post it on the README if you publish it. I don't think that's something we'd bundle into the core of this package right now.

robmadole avatar May 28 '20 21:05 robmadole

I wrote type definitions for this macro: javascripter/fontawesome.macro#1

This is beautiful: image

thorn0 avatar May 29 '20 00:05 thorn0

@thorn0 Thanks. Merged and published to npm πŸ˜„ https://www.npmjs.com/package/fontawesome.macro

@robmadole Yes that'll be great as well. I've written some tests and also setup CI, etc. to make sure my macro continues to work properly.

javascripter avatar May 29 '20 06:05 javascripter

When I use import { solid } from "@fortawesome/fontawesome-svg-core/import.macro";

I get error Module not found: Can't resolve 'fs'

zapbra avatar Aug 14 '22 02:08 zapbra

You can do this using react-fontawesome:

import { faSquare } from '@fortawesome/fontawesome-svg-solid-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

<FontAwesomeIcon icon={faSquare} />

Since you are loading icons one at a time, this is going to be part of a lazy loaded chunk. So, you can just completely ignore using library. I personally like selecting icons based on names:

<FontAwesome icon="square" />

After years of usage I think this is the better way to go about it.

waynevanson avatar Aug 14 '22 05:08 waynevanson