Visitar URL original
Add icon name prop · Issue #41 · feathericons/react-feather · GitHub
Skip to content

Add icon name prop #41

Open
Open

Description

@ffpetrovic

Does it not make sense to have a reusable icon component of our own that utilizes react-feather, and therefore be able to do something like <Icon icon={this.props.icon} />?

I'm aware that we can always pass an icon like <Camera /> down as a prop, but I'm wondering if it's possible to this just by the name of the icon.

Activity

  1. Kosai106 commented on Jul 23, 2019

    @Kosai106

    You could always abstract this kind of behavour, but otherwise doing the following would allow you a similar experience (Almost).

    const IconTag = Icon[this.props.icon];
    
    <IconTag />
  2. SeanMcP commented on Jul 31, 2019

    @SeanMcP

    @Kosai106 I've created a component like that in every project where I've used react-feather. I agree with @ffpetrovic that baking that into the library would be a good idea.

  3. Kosai106 commented on Aug 12, 2019

    @Kosai106

    I just realised that my method wouldn't work if you're using TypeScript anyway.

    Element implicitly has an 'any' type because type 'typeof import("/Users/oesterkilde/projects/[PROJECT-NAME]/node_modules/react-feather/dist/index")' has no index signature

  4. LukyVj commented on Aug 22, 2019

    @LukyVj

    You could always abstract this kind of behavour, but otherwise doing the following would allow you a similar experience (Almost).

    const IconTag = Icon[this.props.icon];
    
    <IconTag />
    

    Thanks for the suggestion @Kosai106, this solved my issue :)

  5. agustingabiola commented on May 4, 2020

    @agustingabiola

    I know this is a bit old but in case anyone needs it I managed to create a component that lazy loads the icons based on the name (you'll need to use lowercase) and works with Typescript:

    
    interface DpFeatherIconProps {
      icon: string;
    }
    
    export default (props: DpFeatherIconProps) => {
      const IconTag = React.lazy(() => import(`react-feather/dist/icons/${props.icon}.js`));
    
      return (
        <Suspense fallback={null}>
          <IconTag />
        </Suspense>
      );
    };
    
  6. kungpaogao commented on Jul 4, 2020

    @kungpaogao

    @agustingabiola Thank you for this workaround. However, the use of lazy/Suspense seems to cause some "blinking" on some loads. Are there any other workarounds?

    This is what I've done to prevent layouts from jumping around as much:

    <Suspense
      fallback={
        <Loader
          color="white"
          className={props.className}
          size={props.size || 12}
        />
      }
    >
    ...
    </Suspense>
  7. Kosai106 commented on Jul 4, 2020

    @Kosai106

    @kungpaogao For the fallback, just use an empty div with the same dimensions as your icon - This should prevent any layout shifting.

  8. kungpaogao commented on Jul 4, 2020

    @kungpaogao

    @Kosai106 yup, that’s what I’m doing already, but it doesn’t solve the “blinking” that happens when the icon is loaded

  9. agustingabiola commented on Jul 5, 2020

    @agustingabiola

    @Kosai106 yup, that’s what I’m doing already, but it doesn’t solve the “blinking” that happens when the icon is loaded

    @kungpaogao Use memo for the component alongside with an element with the same dimensions for fallback. For example if you have a blue cross of 16px you can check that if all props are equal return the same rendered component

  10. kraenhansen commented on Jul 11, 2020

    @kraenhansen

    For anyone interested, I just wrote this functional component which will render an icon by name (in TypeScript):

    import React from "react";
    import * as icons from "react-feather";
    
    export type IconName = keyof typeof icons;
    
    export type IconProps = {
      name: IconName;
    } & icons.Props;
    
    export function Icon({ name, ...rest }: IconProps) {
      const IconComponent = icons[name];
      return <IconComponent {...rest} />;
    }
  11. kungpaogao commented on Jul 11, 2020

    @kungpaogao

    @kraenhansen This is exactly what I'm looking for! Thank you!

  12. osvaldokalvaitir commented on Jul 27, 2020

    @osvaldokalvaitir

    It would be very interesting to be able to pass on the name as property.

  13. LukyVj commented on Nov 5, 2020

    @LukyVj

    Hello 👋

    Nice solution from @kraenhansen, however, using this on next@10 & TypeScript result with the following error:

    Server Error
    Error: Element type is invalid: expected a string (for built-in components) 
    or a class/function (for composite components) but got: undefined.
    You likely forgot to export your component from the file it's defined in, 
    or you might have mixed up default and named imports.
    
    Details Screenshot 2020-11-05 at 10 49 26

    If anyone got a clue or a solution for this.

    Below, my current files:

    Details

    Icon/Icon.tsx

    import React from "react";
    import * as icons from "react-feather";
    
    export type IconName = keyof typeof icons;
    
    export type IconProps = {
      name: IconName;
    } & icons.Props;
    
    export function Icon({ name, ...rest }: IconProps) {
      const IconComponent = icons[name];
      return <IconComponent {...rest} />;
    }

    And where I use the icon:

    import { Icon } from "../Icon/Icon";
    
    ...
    ...
    ...
    {icon && <Icon name={icon} />}

    From what I understand, the exports/imports seems to be wrong somehow, I've tried several ways to fix this but none works, any hint would be appreciated 😄

  14. Kosai106 commented on Nov 5, 2020

    @Kosai106

    @LukyVj It works just fine in Next.js from what I can tell.
    https://codesandbox.io/s/hopeful-franklin-xr1fe?file=/src/pages/index.tsx

    From the error message you're sharing it seems to come from somewhere else.

  15. LukyVj commented on Nov 5, 2020

    @LukyVj

    @LukyVj It works just fine in Next.js from what I can tell.
    codesandbox.io/s/hopeful-franklin-xr1fe?file=/src/pages/index.tsx

    From the error message you're sharing it seems to come from somewhere else.

    Thanks for answering!

    So, yeah, I've tried your code, works well when I pass the name directly as a string Icon name="box" works just fine.

    But, when I tried to pass it through a prop, it doesn't work, e.g:

    Where I define the needed icon

      <Button
            onClick={() => {
              toggleOpen(!open);
            }}
            primary
            className="mb-16"
            icon="Box"
          />

    The button code:

    {icon && <Icon name={icon} />}
    

    So, I've somehow understood why this doesn't work based on your comment, turns out my component get rendered several times including one where the icon is undefined, I suppose that's what's causing the error :D

  16. davidperklin commented on Dec 7, 2020

    @davidperklin

    @LukyVj It works just fine in Next.js from what I can tell.
    codesandbox.io/s/hopeful-franklin-xr1fe?file=/src/pages/index.tsx
    From the error message you're sharing it seems to come from somewhere else.

    Thanks for answering!

    So, yeah, I've tried your code, works well when I pass the name directly as a string Icon name="box" works just fine.

    But, when I tried to pass it through a prop, it doesn't work, e.g:

    Where I define the needed icon

      <Button
            onClick={() => {
              toggleOpen(!open);
            }}
            primary
            className="mb-16"
            icon="Box"
          />

    The button code:

    {icon && <Icon name={icon} />}
    

    So, I've somehow understood why this doesn't work based on your comment, turns out my component get rendered several times including one where the icon is undefined, I suppose that's what's causing the error :D

    Have you figured out a solution for this?

  17. GCobo commented on Dec 13, 2020

    @GCobo

    @kraenhansen thanks!!

  18. lawnchamp commented on Jul 29, 2021

    @lawnchamp

    @kraenhansen doesn't that solution have the problem of all 280 icons being included into your webpack bundle so that you can pick components dynamically? Tree shaking wouldn't be able to work in this case right?

  19. BenSampo commented on Dec 9, 2022

    @BenSampo

    For anyone interested, I just wrote this functional component which will render an icon by name (in TypeScript):

    import React from "react";
    import * as icons from "react-feather";
    
    export type IconName = keyof typeof icons;
    
    export type IconProps = {
      name: IconName;
    } & icons.Props;
    
    export function Icon({ name, ...rest }: IconProps) {
      const IconComponent = icons[name];
      return <IconComponent {...rest} />;
    }

    I have modified as follows, in case it's useful to anyone.

    import React from "react";
    import * as Icons from "react-feather";
    
    export type IconName = keyof typeof Icons;
    
    export type IconProps = {
        name: IconName;
    } & Icons.IconProps;
    
    const Icon: React.FC<IconProps> = ({ name, ...rest }) => {
        const IconComponent = Icons[name];
        return <IconComponent {...rest} />;
    };
    
    export default Icon;

    Usage is <Icon name="Download" />

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions