1

I'm trying to automatically create React Elements from strings corresponding to the react-icons library. But I am getting the following errors in the console:

  • Warning: <RiHeartPulseFill /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
  • Warning: The tag <RiHeartPulseFill> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.

Currently I have a data file that consists of a name and iconName (see below)

const categoriesData = [
    {
        name: 'Vitals',
        iconName: 'RiHeartPulseFill',
    },
    {
        name: 'Body',
        iconName: 'RiBodyScanFill',
    },
    {
        name: 'Sleep',
        iconName: 'RiHotelBedFill',
    },
    {
        name: 'Metabolism',
        iconName: 'RiLungsFill',
    },
    {
        name: 'Stress',
        iconName: 'RiMentalHealthFill',
    },
    {
        name: 'Strength & Training',
        iconName: 'RiRunFill',
    },
    {
        name: 'Lifestyle',
        iconName: 'RiCellphoneFill',
    },
]

export default categoriesData

I want to dynamically render React elements with the exact name as the iconName in the above datafile as React-icons require specific elements with those names.

Then I try to create a list of navigation links (using the React Router <Link> syntax and adding a React-icon + Name. See the code below:

const menuCategories = categoriesData.map((category) => {
        const IconElement = category.iconName

        return (
            <Link
                to={`/data/${category.name.toLowerCase()}`}
                key={category.name}
                className="flex flex-row items-center gap-2"
            >
                <IconElement />
                {category.name}
            </Link>
        )
    })

The issue I run into is the following error: Warning: <RiHeartPulseFill /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.

I does not seems to be incorrect as it actually IS PascalCase. However when I check dev tools I see the following: <riheartpulsefill></riheartpulsefill>

I have no Idea why this happens. Any solutions?

Extra: Does anyone know how I can also import those icon names based on the initial data files. I'm thinking about creating an icon selection tool, so only the selected icons should be imported from the react-icons lib.

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
Jeroen
  • 11
  • 1
  • 3

2 Answers2

1

If you want to dynamically render these icon components then you'll typically need to import and specify them in the config instead of strings corresponding to their names.

Example:

import {
  RiHeartPulseFill,
  RiBodyScanFill,
  RiHotelBedFill,
  RiLungsFill,
  RiMentalHealthFill,
  RiRunFill,
  RiCellphoneFill,
} from "react-icons/ri";

const categoriesData = [
  {
    name: 'Vitals',
    iconName: RiHeartPulseFill,
  },
  {
    name: 'Body',
    iconName: RiBodyScanFill,
  },
  {
    name: 'Sleep',
    iconName: RiHotelBedFill,
  },
  {
    name: 'Metabolism',
    iconName: RiLungsFill,
  },
  {
    name: 'Stress',
    iconName: RiMentalHealthFill,
  },
  {
    name: 'Strength & Training',
    iconName: RiRunFill,
  },
  {
    name: 'Lifestyle',
    iconName: RiCellphoneFill,
  },
];

export default categoriesData;
const menuCategories = categoriesData.map((category) => {
  const IconElement = category.iconName;

  return (
    <Link
      to={`/data/${category.name.toLowerCase()}`}
      key={category.name}
      className="flex flex-row items-center gap-2"
    >
      <IconElement />
      {category.name}
    </Link>
  );
});

An alternative is to create and export a lookup object for the icon components.

import {
  RiHeartPulseFill,
  RiBodyScanFill,
  RiHotelBedFill,
  RiLungsFill,
  RiMentalHealthFill,
  RiRunFill,
  RiCellphoneFill,
} from "react-icons/ri";

export const iconMap = {
  RiHeartPulseFill,
  RiBodyScanFill,
  RiHotelBedFill,
  RiLungsFill,
  RiMentalHealthFill,
  RiRunFill,
  RiCellphoneFill,
};

const categoriesData = [
  {
    name: 'Vitals',
    iconName: 'RiHeartPulseFill',
  },
  {
    name: 'Body',
    iconName: 'RiBodyScanFill',
  },
  {
    name: 'Sleep',
    iconName: 'RiHotelBedFill',
  },
  {
    name: 'Metabolism',
    iconName: 'RiLungsFill',
  },
  {
    name: 'Stress',
    iconName: 'RiMentalHealthFill',
  },
  {
    name: 'Strength & Training',
    iconName: 'RiRunFill',
  },
  {
    name: 'Lifestyle',
    iconName: 'RiCellphoneFill',
  },
];

export default categoriesData;
const menuCategories = categoriesData.map((category) => {
  const IconElement = iconMap[category.iconName];

  return (
    <Link
      to={`/data/${category.name.toLowerCase()}`}
      key={category.name}
      className="flex flex-row items-center gap-2"
    >
      <IconElement />
      {category.name}
    </Link>
  );
});

To allow for any react-icons/ri icon then in the UI component import all of react-icons/ri and conditionally render the icon component if it exists.

import { Link } from 'react-router-dom';
import * as ReactRiIcons from "react-icons/ri"; // <-- all RI icons
import * as ReactRxIcons from "react-icons/rx"; // <-- all RX icons

const ReactIcons = { // <-- all merged icons set
  ...ReactRiIcons,
  ...ReactRxIcons
};

...

const menuCategories = categoriesData.map((category) => {
  const IconElement = ReactIcons[category.iconName];

  return (
    <Link
      to={`/data/${category.name.toLowerCase()}`}
      key={category.name}
      className="flex flex-row items-center gap-2"
    >
      {IconElement && <IconElement />} // <-- handle possible undefined icon
      {category.name}
    </Link>
  );
});

...

Edit warning-element-is-using-incorrect-casing-use-pascalcase-for-react-compone

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Thanks Drew, would it be possible to make the imports/exports dynamic as I do not know beforehand what icons will be chosen by the users? – Jeroen Jan 28 '23 at 11:31
  • @Jeroen This is how you render dynamic components at runtime. This isn't really about knowing what a user has selected, it's about the app being ready to render anything a user can specify. Is your follow on question more about dynamic imports and lazy component loading/rendering of the icon components? – Drew Reese Jan 28 '23 at 22:51
  • In my example, I have a list with fixed icon names, however, I want to build this in a way that users can choose any icon name from the react-icons lib and that I can render those automatically. This will mean that I need to automatically import & export those (as I used your alternative/second solution). Is there a way to do this dynamically? So for example when a user chooses the 'RxArrowLeft' icon that this icon will automatically be added to both the import as well as the export statement ? Hope this explains a bit more clearly what I want to implement – Jeroen Jan 30 '23 at 09:41
  • @Jeroen It does. Please check my updated answer and linked sandbox. – Drew Reese Jan 30 '23 at 17:01
0

Use React.createElement. Take a look here to see how: Create react component dynamically

Heres my recursive example:

const demoData = [
{
    tagName: 'MyButtonComponent',
    children: [
        {
            tagName: 'MyChildComponent'
        }
    ]
},
{
    tagName: 'MyOtherComponent'
},
]


function recursivelyRenderChildren(elements) {
    if(elements.length) {
        return elements.map((element, index) => {
            return React.createElement(elementData.tagName, {
                key: element.fieldType+'-'+index,
                children: recursivelyRenderChildren(element.children)
            });
        })
    }
}

const arrayOfElements = recursivelyRenderChildren(demoData)