Repository: lucide-icons/lucide
Pull request #2878: fix(packages): consistent icon name class
Author: login: danteissaias
Created: 2025-03-08T01:35:37Z | Merged: 2025-03-21T10:35:54Z

Closes #2858

## What is the purpose of this pull request?
- [ ] New Icon
- [x] Bug fix
- [ ] New Feature
- [ ] Documentation update
- [ ] Other:

### Description

Thanks for taking the time to look at my previous PR!

Based on our previous discussion, I've implemented a different solution. Instead of modifying the `toKebabCase` function, I've added a new class that's derived from the icon's name on the filesystem.

The good news is that this change is fully backwards compatible. The new classes will follow this pattern:
```lucide lucide-grid3x3 lucide-grid-3x3```

This means existing implementations using the current class names will continue to work without any changes needed.

Looking forward to your feedback on this approach.

## Before Submitting
- [x] I've read the [Contribution Guidelines](https://github.com/lucide-icons/lucide/blob/main/CONTRIBUTING.md).
- [x] I've checked if there was an existing PR that solves the same issue.


Linked issue #2858: Inconsistent kebab-casing between class attribute and iconNames
### Package

- [x] lucide
- [ ] lucide-angular
- [ ] lucide-flutter
- [ ] lucide-preact
- [x] lucide-react
- [ ] lucide-react-native
- [ ] lucide-solid
- [ ] lucide-svelte
- [ ] lucide-vue
- [ ] lucide-vue-next
- [ ] Figma plugin
- [ ] source/main
- [ ] other/not relevant

### Version

0.477.0

### Can you reproduce this in the latest version?

- [x] Yes
- [ ] No

### Browser

- [ ] Chrome/Chromium
- [ ] Firefox
- [ ] Safari
- [ ] Edge
- [ ] iOS Safari
- [ ] Opera
- [x] Other/not relevant

### Operating system

- [ ] Windows
- [ ] Linux
- [ ] macOS
- [ ] ChromeOS
- [ ] iOS
- [ ] Android
- [x] Other/not relevant

### Description

When rendering an icon with `lucide-react`, the kebab-cased class name is inconsistent with the corresponding entry in `iconNames`.

Example with `AArrowDown` icon:
- Class attribute: `class="lucide lucide-aarrow-down"`
- In `iconNames`: `a-arrow-down`

Example with `Loader2` icon:
- Class attribute: `class="lucide lucide-loader2"`
- In `iconNames`: `loader-2`

Root cause appears to be in the `toKebabCase` function.

### Steps to reproduce

N/A

### Checklist

- [x] I have searched if someone has submitted a similar issue before and there weren't any. (Please make sure to also search closed issues, as this issue might already have been resolved.)

Diff:
diff --git a/packages/lucide-preact/scripts/exportTemplate.mjs b/packages/lucide-preact/scripts/exportTemplate.mjs
index 42a9be1ffac..1608ac7b774 100644
--- a/packages/lucide-preact/scripts/exportTemplate.mjs
+++ b/packages/lucide-preact/scripts/exportTemplate.mjs
@@ -26,7 +26,7 @@ import createLucideIcon from '../createLucideIcon';
  * @returns {JSX.Element} JSX Element
  * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
  */
-const ${componentName} = createLucideIcon('${componentName}', ${JSON.stringify(children)});
+const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)});
 
 export default ${componentName};
 `;
diff --git a/packages/lucide-preact/src/createLucideIcon.ts b/packages/lucide-preact/src/createLucideIcon.ts
index 2ba13086f3f..b2e2fb3009b 100644
--- a/packages/lucide-preact/src/createLucideIcon.ts
+++ b/packages/lucide-preact/src/createLucideIcon.ts
@@ -1,5 +1,5 @@
 import { h, type JSX } from 'preact';
-import { mergeClasses, toKebabCase } from '@lucide/shared';
+import { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';
 import Icon from './Icon';
 import type { IconNode, LucideIcon, LucideProps } from './types';
 
@@ -17,6 +17,7 @@ const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
         ...props,
         iconNode,
         class: mergeClasses<string | JSX.SignalLike<string | undefined>>(
+          `lucide-${toKebabCase(toPascalCase(iconName))}`,
           `lucide-${toKebabCase(iconName)}`,
           classes,
         ),
@@ -24,7 +25,7 @@ const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
       children,
     );
 
-  Component.displayName = `${iconName}`;
+  Component.displayName = toPascalCase(iconName);
 
   return Component;
 };
diff --git a/packages/lucide-preact/tests/__snapshots__/createLucideIcon.spec.tsx.snap b/packages/lucide-preact/tests/__snapshots__/createLucideIcon.spec.tsx.snap
index acf4f973908..aeb02b39527 100644
--- a/packages/lucide-preact/tests/__snapshots__/createLucideIcon.spec.tsx.snap
+++ b/packages/lucide-preact/tests/__snapshots__/createLucideIcon.spec.tsx.snap
@@ -27,3 +27,59 @@ exports[`Using createLucideIcon > should create a component from an iconNode 1`]
   />
 </svg>
 `;
+
+exports[`Using createLucideIcon > should create a component from an iconNode with iconName 1`] = `
+<svg
+  class="lucide lucide-air-vent"
+  fill="none"
+  height="24"
+  stroke="currentColor"
+  stroke-linecap="round"
+  stroke-linejoin="round"
+  stroke-width="2"
+  viewBox="0 0 24 24"
+  width="24"
+  xmlns="http://www.w3.org/2000/svg"
+>
+  <path
+    d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
+  />
+  <path
+    d="M6 8h12"
+  />
+  <path
+    d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
+  />
+  <path
+    d="M6.6 15.6A2 2 0 1 0 10 17v-5"
+  />
+</svg>
+`;
+
+exports[`Using createLucideIcon > should include backwards compatible className 1`] = `
+<svg
+  class="lucide lucide-layout2 lucide-layout-2"
+  fill="none"
+  height="24"
+  stroke="currentColor"
+  stroke-linecap="round"
+  stroke-linejoin="round"
+  stroke-width="2"
+  viewBox="0 0 24 24"
+  width="24"
+  xmlns="http://www.w3.org/2000/svg"
+>
+  <path
+    d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
+  />
+  <path
+    d="M6 8h12"
+  />
+  <path
+    d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
+  />
+  <path
+    d="M6.6 15.6A2 2 0 1 0 10 17v-5"
+  />
+</svg>
+`;
diff --git a/packages/lucide-preact/tests/__snapshots__/lucide-preact.spec.tsx.snap b/packages/lucide-preact/tests/__snapshots__/lucide-preact.spec.tsx.snap
index a3cc0865779..eacfae32021 100644
--- a/packages/lucide-preact/tests/__snapshots__/lucide-preact.spec.tsx.snap
+++ b/packages/lucide-preact/tests/__snapshots__/lucide-preact.spec.tsx.snap
@@ -10,7 +10,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
      stroke-width="4"
      stroke-linecap="round"
      stroke-linejoin="round"
-     class="lucide lucide-grid3x3"
+     class="lucide lucide-grid3x3 lucide-grid-3x3"
 >
   <rect width="18"
         height="18"
@@ -40,7 +40,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
      stroke-width="1"
      stroke-linecap="round"
      stroke-linejoin="round"
-     class="lucide lucide-grid3x3"
+     class="lucide lucide-grid3x3 lucide-grid-3x3"
 >
   <rect width="18"
         height="18"
@@ -70,7 +70,7 @@ exports[`Using lucide icon components > should render an component 1`] = `
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
-     class="lucide lucide-grid3x3"
+     class="lucide lucide-grid3x3 lucide-grid-3x3"
 >
   <rect width="18"
         height="18"
diff --git a/packages/lucide-preact/tests/createLucideIcon.spec.tsx b/packages/lucide-preact/tests/createLucideIcon.spec.tsx
index 883f6f424dd..bab081ff065 100644
--- a/packages/lucide-preact/tests/createLucideIcon.spec.tsx
+++ b/packages/lucide-preact/tests/createLucideIcon.spec.tsx
@@ -12,4 +12,22 @@ describe('Using createLucideIcon', () => {
     expect(container.firstChild).toMatchSnapshot();
     expect(container.firstChild).toBeDefined();
   });
+
+  it('should create a component from an iconNode with iconName', () => {
+    const AirVent = createLucideIcon('air-vent', airVent);
+
+    const { container } = render(<AirVent />);
+
+    expect(container.firstChild).toMatchSnapshot();
+    expect(container.firstChild).toBeDefined();
+  });
+
+  it('should include backwards compatible className', () => {
+    const Layout2 = createLucideIcon('layout-2', airVent);
+
+    const { container } = render(<Layout2 />);
+
+    expect(container.firstChild).toMatchSnapshot();
+    expect(container.firstChild).toBeDefined();
+  });
 });
diff --git a/packages/lucide-react/scripts/exportTemplate.mjs b/packages/lucide-react/scripts/exportTemplate.mjs
index e39cf0a6fee..167d858a0be 100644
--- a/packages/lucide-react/scripts/exportTemplate.mjs
+++ b/packages/lucide-react/scripts/exportTemplate.mjs
@@ -29,7 +29,7 @@ export const __iconNode: IconNode = ${JSON.stringify(children)}
  * @returns {JSX.Element} JSX Element
  * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
  */
-const ${componentName} = createLucideIcon('${componentName}', __iconNode);
+const ${componentName} = createLucideIcon('${iconName}', __iconNode);
 
 export default ${componentName};
 `;
diff --git a/packages/lucide-react/src/createLucideIcon.ts b/packages/lucide-react/src/createLucideIcon.ts
index b209293824a..bc453af84fb 100644
--- a/packages/lucide-react/src/createLucideIcon.ts
+++ b/packages/lucide-react/src/createLucideIcon.ts
@@ -1,5 +1,5 @@
 import { createElement, forwardRef } from 'react';
-import { mergeClasses, toKebabCase } from '@lucide/shared';
+import { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';
 import { IconNode, LucideProps } from './types';
 import Icon from './Icon';
 
@@ -14,12 +14,16 @@ const createLucideIcon = (iconName: string, iconNode: IconNode) => {
     createElement(Icon, {
       ref,
       iconNode,
-      className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),
+      className: mergeClasses(
+        `lucide-${toKebabCase(toPascalCase(iconName))}`,
+        `lucide-${iconName}`,
+        className,
+      ),
       ...props,
     }),
   );
 
-  Component.displayName = `${iconName}`;
+  Component.displayName = toPascalCase(iconName);
 
   return Component;
 };
diff --git a/packages/lucide-react/tests/__snapshots__/createLucideIcon.spec.tsx.snap b/packages/lucide-react/tests/__snapshots__/createLucideIcon.spec.tsx.snap
index acf4f973908..a1f9dc728ba 100644
--- a/packages/lucide-react/tests/__snapshots__/createLucideIcon.spec.tsx.snap
+++ b/packages/lucide-react/tests/__snapshots__/createLucideIcon.spec.tsx.snap
@@ -1,6 +1,34 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`Using createLucideIcon > should create a component from an iconNode 1`] = `
+<svg
+  class="lucide lucide-air-vent lucide-AirVent"
+  fill="none"
+  height="24"
+  stroke="currentColor"
+  stroke-linecap="round"
+  stroke-linejoin="round"
+  stroke-width="2"
+  viewBox="0 0 24 24"
+  width="24"
+  xmlns="http://www.w3.org/2000/svg"
+>
+  <path
+    d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
+  />
+  <path
+    d="M6 8h12"
+  />
+  <path
+    d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
+  />
+  <path
+    d="M6.6 15.6A2 2 0 1 0 10 17v-5"
+  />
+</svg>
+`;
+
+exports[`Using createLucideIcon > should create a component from an iconNode with iconName 1`] = `
 <svg
   class="lucide lucide-air-vent"
   fill="none"
@@ -27,3 +55,31 @@ exports[`Using createLucideIcon > should create a component from an iconNode 1`]
   />
 </svg>
 `;
+
+exports[`Using createLucideIcon > should include backwards compatible className 1`] = `
+<svg
+  class="lucide lucide-layout2 lucide-layout-2"
+  fill="none"
+  height="24"
+  stroke="currentColor"
+  stroke-linecap="round"
+  stroke-linejoin="round"
+  stroke-width="2"
+  viewBox="0 0 24 24"
+  width="24"
+  xmlns="http://www.w3.org/2000/svg"
+>
+  <path
+    d="M6 12H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"
+  />
+  <path
+    d="M6 8h12"
+  />
+  <path
+    d="M18.3 17.7a2.5 2.5 0 0 1-3.16 3.83 2.53 2.53 0 0 1-1.14-2V12"
+  />
+  <path
+    d="M6.6 15.6A2 2 0 1 0 10 17v-5"
+  />
+</svg>
+`;
diff --git a/packages/lucide-react/tests/__snapshots__/lucide-react.spec.tsx.snap b/packages/lucide-react/tests/__snapshots__/lucide-react.spec.tsx.snap
index a3cc0865779..eacfae32021 100644
--- a/packages/lucide-react/tests/__snapshots__/lucide-react.spec.tsx.snap
+++ b/packages/lucide-react/tests/__snapshots__/lucide-react.spec.tsx.snap
@@ -10,7 +10,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
      stroke-width="4"
      stroke-linecap="round"
      stroke-linejoin="round"
-     class="lucide lucide-grid3x3"
+     class="lucide lucide-grid3x3 lucide-grid-3x3"
 >
   <rect width="18"
         height="18"
@@ -40,7 +40,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
      stroke-width="1"
      stroke-linecap="round"
      stroke-linejoin="round"
-     class="lucide lucide-grid3x3"
+     class="lucide lucide-grid3x3 lucide-grid-3x3"
 >
   <rect width="18"
         height="18"
@@ -70,7 +70,7 @@ exports[`Using lucide icon components > should render an component 1`] = `
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
-     class="lucide lucide-grid3x3"
+     class="lucide lucide-grid3x3 lucide-grid-3x3"
 >
   <rect width="18"
         height="18"
diff --git a/packages/lucide-react/tests/createLucideIcon.spec.tsx b/packages/lucide-react/tests/createLucideIcon.spec.tsx
index 7c1b5e475bd..051bc579ebb 100644
--- a/packages/lucide-react/tests/createLucideIcon.spec.tsx
+++ b/packages/lucide-react/tests/createLucideIcon.spec.tsx
@@ -12,4 +12,22 @@ describe('Using createLucideIcon', () => {
     expect(container.firstChild).toMatchSnapshot();
     expect(container.firstChild).toBeDefined();
   });
+
+  it('should create a component from an iconNode with iconName', () => {
+    const AirVent = createLucideIcon('air-vent', airVent);
+
+    const { container } = render(<AirVent />);
+
+    expect(container.firstChild).toMatchSnapshot();
+    expect(container.firstChild).toBeDefined();
+  });
+
+  it('should include backwards compatible className', () => {
+    const Layout2 = createLucideIcon('layout-2', airVent);
+
+    const { container } = render(<Layout2 />);
+
+    expect(container.firstChild).toMatchSnapshot();
+    expect(container.firstChild).toBeDefined();
+  });
 });
diff --git a/packages/lucide-solid/scripts/exportTemplate.mjs b/packages/lucide-solid/scripts/exportTemplate.mjs
index 116b257b5c4..5c1a4a68691 100644
--- a/packages/lucide-solid/scripts/exportTemplate.mjs
+++ b/packages/lucide-solid/scripts/exportTemplate.mjs
@@ -30,7 +30,7 @@ const iconNode: IconNode = ${JSON.stringify(children)};
  * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
  */
 const ${componentName} = (props: LucideProps) => (
-  <Icon {...props} name="${componentName}" iconNode={iconNode} />
+  <Icon {...props} iconNode={iconNode} name="${iconName}" />
 )
 
 export default ${componentName};
diff --git a/packages/lucide-solid/src/Icon.tsx b/packages/lucide-solid/src/Icon.tsx
index d805f72ca4a..fb3f077133c 100644
--- a/packages/lucide-solid/src/Icon.tsx
+++ b/packages/lucide-solid/src/Icon.tsx
@@ -2,7 +2,7 @@ import { For, splitProps } from 'solid-js';
 import { Dynamic } from 'solid-js/web';
 import defaultAttributes from './defaultAttributes';
 import { IconNode, LucideProps } from './types';
-import { mergeClasses, toKebabCase } from '@lucide/shared';
+import { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';
 
 interface IconProps {
   name?: string;
@@ -36,7 +36,12 @@ const Icon = (props: LucideProps & IconProps) => {
       class={mergeClasses(
         'lucide',
         'lucide-icon',
-        localProps.name != null ? `lucide-${toKebabCase(localProps?.name)}` : undefined,
+        ...(localProps.name != null
+          ? [
+              `lucide-${toKebabCase(toPascalCase(localProps.name))}`,
+              `lucide-${toKebabCase(localProps.name)}`,
+            ]
+          : []),
         localProps.class != null ? localProps.class : '',
       )}
       {...rest}
diff --git a/packages/lucide-solid/tests/__snapshots__/lucide-solid.spec.tsx.snap b/packages/lucide-solid/tests/__snapshots__/lucide-solid.spec.tsx.snap
index 47ba999bfaa..73f49add462 100644
--- a/packages/lucide-solid/tests/__snapshots__/lucide-solid.spec.tsx.snap
+++ b/packages/lucide-solid/tests/__snapshots__/lucide-solid.spec.tsx.snap
@@ -10,7 +10,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
      height="48"
      stroke="red"
      stroke-width="4"
-     class="lucide lucide-icon lucide-grid3x3"
+     class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3"
      data-testid="grid-icon"
 >
   <rect width="18"
@@ -50,7 +50,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
      height="48"
      stroke="red"
      stroke-width="1"
-     class="lucide lucide-icon lucide-grid3x3"
+     class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3"
      data-testid="grid-icon"
 >
   <rect width="18"
@@ -90,7 +90,7 @@ exports[`Using lucide icon components > should render a component 1`] = `
      height="24"
      stroke="currentColor"
      stroke-width="2"
-     class="lucide lucide-icon lucide-grid3x3"
+     class="lucide lucide-icon lucide-grid3x3 lucide-grid-3x3"
 >
   <rect width="18"
         height="18"
diff --git a/packages/lucide-vue-next/scripts/exportTemplate.mjs b/packages/lucide-vue-next/scripts/exportTemplate.mjs
index e7550a667fd..e37c90a9ac1 100644
--- a/packages/lucide-vue-next/scripts/exportTemplate.mjs
+++ b/packages/lucide-vue-next/scripts/exportTemplate.mjs
@@ -26,7 +26,7 @@ import createLucideIcon from '../createLucideIcon';
  * @returns {FunctionalComponent} Vue component
  * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
  */
-const ${componentName} = createLucideIcon('${componentName}Icon', ${JSON.stringify(children)});
+const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)});
 
 export default ${componentName};
 `;
diff --git a/packages/lucide-vue-next/src/Icon.ts b/packages/lucide-vue-next/src/Icon.ts
index 3b33bc18948..f0a7b51904f 100644
--- a/packages/lucide-vue-next/src/Icon.ts
+++ b/packages/lucide-vue-next/src/Icon.ts
@@ -1,5 +1,5 @@
 import { type FunctionalComponent, h } from 'vue';
-import { toKebabCase } from '@lucide/shared';
+import { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';
 import defaultAttributes from './defaultAttributes';
 import { IconNode, LucideProps } from './types';
 
@@ -20,7 +20,12 @@ const Icon: FunctionalComponent<LucideProps & IconProps> = (
       height: size || defaultAttributes.height,
       stroke: color || defaultAttributes.stroke,
       'stroke-width': absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
-      class: ['lucide', `lucide-${toKebabCase(name ?? 'icon')}`],
+      class: mergeClasses(
+        'lucide',
+        ...(name
+          ? [`lucide-${toKebabCase(toPascalCase(name))}-icon`, `lucide-${toKebabCase(name)}`]
+          : ['lucide-icon']),
+      ),
       ...props,
     },
     [...iconNode.map((child) => h(...child)), ...(slots.default ? [slots.default()] : [])],
diff --git a/packages/lucide-vue-next/tests/__snapshots__/lucide-vue-next.spec.ts.snap b/packages/lucide-vue-next/tests/__snapshots__/lucide-vue-next.spec.ts.snap
index c9e6de19709..88156ce1b83 100644
--- a/packages/lucide-vue-next/tests/__snapshots__/lucide-vue-next.spec.ts.snap
+++ b/packages/lucide-vue-next/tests/__snapshots__/lucide-vue-next.spec.ts.snap
@@ -3,7 +3,7 @@
 exports[`Using lucide icon components > should add a class to the element 1`] = `
 <div>
   <svg
-    class="lucide lucide-smile-icon my-icon"
+    class="lucide lucide-smile-icon lucide-smile my-icon"
     fill="none"
     height="24"
     stroke="currentColor"
@@ -41,7 +41,7 @@ exports[`Using lucide icon components > should add a class to the element 1`] =
 exports[`Using lucide icon components > should add a style attribute to the element 1`] = `
 <div>
   <svg
-    class="lucide lucide-smile-icon"
+    class="lucide lucide-smile-icon lucide-smile"
     fill="none"
     height="24"
     stroke="currentColor"
@@ -80,7 +80,7 @@ exports[`Using lucide icon components > should add a style attribute to the elem
 exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = `
 <div>
   <svg
-    class="lucide lucide-smile-icon"
+    class="lucide lucide-smile-icon lucide-smile"
     fill="none"
     height="48"
     stroke="red"
@@ -118,7 +118,7 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
 exports[`Using lucide icon components > should pass children to the icon slot 1`] = `
 <div>
   <svg
-    class="lucide lucide-smile-icon"
+    class="lucide lucide-smile-icon lucide-smile"
     fill="none"
     height="24"
     stroke="currentColor"
@@ -161,7 +161,7 @@ exports[`Using lucide icon components > should pass children to the icon slot 1`
 exports[`Using lucide icon components > should render an component 1`] = `
 <div>
   <svg
-    class="lucide lucide-smile-icon"
+    class="lucide lucide-smile-icon lucide-smile"
     fill="none"
     height="24"
     stroke="currentColor"


Reviews:
user: login: jguddas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-08T08:15:20Z
user: login: jguddas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-08T08:21:22Z
user: login: danteissaias
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-08T11:13:10Z
user: login: jguddas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-08T11:33:42Z
user: login: danteissaias
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-08T11:48:21Z
user: login: ericfennis
body: Really nice work, thanks for fixing this!
I've found something we need to check. See comment below
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-14T14:31:46Z
user: login: danteissaias
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T14:35:30Z
user: login: danteissaias
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T14:48:40Z
user: login: ericfennis
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-21T10:21:54Z
user: login: danteissaias
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T10:27:00Z
user: login: ericfennis
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-21T10:33:27Z
user: login: ericfennis
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-21T10:35:44Z

Review comments:
diff_hunk: @@ -5,26 +5,32 @@ import type { IconNode, LucideIcon, LucideProps } from './types';
 
 /**
  * Create a Lucide icon component
- * @param {string} iconName
+ * @param {string} componentName
  * @param {array} iconNode
+ * @param {string} iconName
  * @returns {FunctionComponent} LucideIcon
  */
-const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
+const createLucideIcon = (
+  componentName: string,
+  iconNode: IconNode,
+  iconName: string,
path: packages/lucide-preact/src/createLucideIcon.ts
user: login: jguddas
body: This is a breaking change, what you could do is make the third parameter optional.
People use `createLucideIcon`, it's not only used internally.
created_at: 2025-03-08T08:15:20Z
author_association: MEMBER
diff_hunk: @@ -5,26 +5,32 @@ import type { IconNode, LucideIcon, LucideProps } from './types';
 
 /**
  * Create a Lucide icon component
- * @param {string} iconName
+ * @param {string} componentName
  * @param {array} iconNode
+ * @param {string} iconName
  * @returns {FunctionComponent} LucideIcon
  */
-const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
+const createLucideIcon = (
+  componentName: string,
+  iconNode: IconNode,
+  iconName: string,
+): LucideIcon => {
   const Component = ({ class: classes = '', children, ...props }: LucideProps) =>
     h(
       Icon,
       {
         ...props,
         iconNode,
         class: mergeClasses<string | JSX.SignalLike<string | undefined>>(
-          `lucide-${toKebabCase(iconName)}`,
+          `lucide-${toKebabCase(componentName)}`,
+          `lucide-${iconName}`,
path: packages/lucide-preact/src/createLucideIcon.ts
user: login: jguddas
body: Alternatively we could make it, so the first argument is the actual iconName in kebabCase.
```suggestion
          `lucide-${toKebabCase(toPascalCase(iconName))}`,
          `lucide-${toKebabCase(iconName)}`
```
created_at: 2025-03-08T08:20:59Z
author_association: MEMBER
diff_hunk: @@ -5,26 +5,32 @@ import type { IconNode, LucideIcon, LucideProps } from './types';
 
 /**
  * Create a Lucide icon component
- * @param {string} iconName
+ * @param {string} componentName
  * @param {array} iconNode
+ * @param {string} iconName
  * @returns {FunctionComponent} LucideIcon
  */
-const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
+const createLucideIcon = (
+  componentName: string,
+  iconNode: IconNode,
+  iconName: string,
+): LucideIcon => {
   const Component = ({ class: classes = '', children, ...props }: LucideProps) =>
     h(
       Icon,
       {
         ...props,
         iconNode,
         class: mergeClasses<string | JSX.SignalLike<string | undefined>>(
-          `lucide-${toKebabCase(iconName)}`,
+          `lucide-${toKebabCase(componentName)}`,
+          `lucide-${iconName}`,
           classes,
         ),
       },
       children,
     );
 
-  Component.displayName = `${iconName}`;
+  Component.displayName = `${componentName}`;
path: packages/lucide-preact/src/createLucideIcon.ts
user: login: jguddas
body: You would have to change quite a few more places tho.
```suggestion
  Component.displayName = toPascalCase(iconName);
```
created_at: 2025-03-08T08:21:14Z
author_association: MEMBER
diff_hunk: @@ -5,26 +5,32 @@ import type { IconNode, LucideIcon, LucideProps } from './types';
 
 /**
  * Create a Lucide icon component
- * @param {string} iconName
+ * @param {string} componentName
  * @param {array} iconNode
+ * @param {string} iconName
  * @returns {FunctionComponent} LucideIcon
  */
-const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
+const createLucideIcon = (
+  componentName: string,
+  iconNode: IconNode,
+  iconName: string,
path: packages/lucide-preact/src/createLucideIcon.ts
user: login: danteissaias
body: Ah thanks for pointing out, I wasn't aware this was a public API. I'm down to go with your proposed solution of making the first argument the actual icon name.
created_at: 2025-03-08T11:13:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -5,7 +5,7 @@ import { render } from '@testing-library/preact';
 
 describe('Using createLucideIcon', () => {
   it('should create a component from an iconNode', () => {
-    const AirVent = createLucideIcon('AirVent', airVent, 'air-vent');
+    const AirVent = createLucideIcon('air-vent', airVent);
path: packages/lucide-preact/tests/createLucideIcon.spec.tsx
user: login: jguddas
body: I think the test here should stay the same.
```suggestion
    const AirVent = createLucideIcon('AirVent', airVent);
```

You can add a second test for `air-vent` and `layout-2`.
created_at: 2025-03-08T11:33:42Z
author_association: MEMBER
diff_hunk: @@ -5,7 +5,7 @@ import { render } from '@testing-library/preact';
 
 describe('Using createLucideIcon', () => {
   it('should create a component from an iconNode', () => {
-    const AirVent = createLucideIcon('AirVent', airVent, 'air-vent');
+    const AirVent = createLucideIcon('air-vent', airVent);
path: packages/lucide-preact/tests/createLucideIcon.spec.tsx
user: login: danteissaias
body: Agreed
created_at: 2025-03-08T11:48:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -26,7 +26,7 @@ import createLucideIcon from '../createLucideIcon';
  * @returns {FunctionalComponent} Vue component
  * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
  */
-const ${componentName} = createLucideIcon('${componentName}Icon', ${JSON.stringify(children)});
+const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)});
path: packages/lucide-vue-next/scripts/exportTemplate.mjs
user: login: ericfennis
body: I want to have this the same name as the other packges. But unfortunately this is an issue with "reserved HMTL tags" for components in Vue. For example the Icon `filter` is conflicting with HTML tag fllter.
See issue #515
But not sure if this is still and issue in Vue, need to be checked
created_at: 2025-03-14T14:30:01Z
author_association: MEMBER
diff_hunk: @@ -26,7 +26,7 @@ import createLucideIcon from '../createLucideIcon';
  * @returns {FunctionalComponent} Vue component
  * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
  */
-const ${componentName} = createLucideIcon('${componentName}Icon', ${JSON.stringify(children)});
+const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)});
path: packages/lucide-vue-next/scripts/exportTemplate.mjs
user: login: danteissaias
body: I don't see this as a problem in `vue-next` since we're just using `name` for the class. In the Vue 2.0 package, we were also using `name` for the component name, but I left that package alone since it's deprecated anyway.
created_at: 2025-03-14T14:35:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -26,7 +26,7 @@ import createLucideIcon from '../createLucideIcon';
  * @returns {FunctionalComponent} Vue component
  * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
  */
-const ${componentName} = createLucideIcon('${componentName}Icon', ${JSON.stringify(children)});
+const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)});
path: packages/lucide-vue-next/scripts/exportTemplate.mjs
user: login: danteissaias
body: Just tested and am not getting any issues.
<img width="555" alt="Screenshot 2025-03-14 at 14 46 48" src="https://github.com/user-attachments/assets/f76af79b-4a84-4985-a486-e08ca3ee6336" />

created_at: 2025-03-14T14:48:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -26,7 +26,7 @@ import createLucideIcon from '../createLucideIcon';
  * @returns {FunctionalComponent} Vue component
  * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
  */
-const ${componentName} = createLucideIcon('${componentName}Icon', ${JSON.stringify(children)});
+const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)});
path: packages/lucide-vue-next/scripts/exportTemplate.mjs
user: login: ericfennis
body: Also no logs and errors in console or terminal?
created_at: 2025-03-21T10:21:54Z
author_association: MEMBER
diff_hunk: @@ -26,7 +26,7 @@ import createLucideIcon from '../createLucideIcon';
  * @returns {FunctionalComponent} Vue component
  * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
  */
-const ${componentName} = createLucideIcon('${componentName}Icon', ${JSON.stringify(children)});
+const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)});
path: packages/lucide-vue-next/scripts/exportTemplate.mjs
user: login: danteissaias
body: Nope!
created_at: 2025-03-21T10:27:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -26,7 +26,7 @@ import createLucideIcon from '../createLucideIcon';
  * @returns {FunctionalComponent} Vue component
  * ${deprecated ? `@deprecated ${deprecationReason}` : ''}
  */
-const ${componentName} = createLucideIcon('${componentName}Icon', ${JSON.stringify(children)});
+const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)});
path: packages/lucide-vue-next/scripts/exportTemplate.mjs
user: login: ericfennis
body: I've checked this with a fresh Vue project, no issues. So no issue anymore 👍🏼
created_at: 2025-03-21T10:33:27Z
author_association: MEMBER

PR comments:
user: login: danteissaias
created_at: 2025-03-14T13:57:23Z
body: Do you know why the Vercel build is failing after I merged main?
author_association: CONTRIBUTOR
user: login: danteissaias
created_at: 2025-03-21T10:37:23Z
body: 🎉
author_association: CONTRIBUTOR