feat(packages): extract icon build logic into @lucide/shared (#4409)

* feat(packages): extract build logic to @lucide/shared, still wip

* feat(): unify icon build logic some more, add nonScalingStroke option, marking absoluteStrokeWidth as deprecated

* feat(): unify icon build logic some more, add nonScalingStroke option, marking absoluteStrokeWidth as deprecated

* feat(packages): extract build logic to @lucide/shared, still wip

* feat(): unify icon build logic some more, add nonScalingStroke option, marking absoluteStrokeWidth as deprecated

* feat(): unify icon build logic some more, add nonScalingStroke option, marking absoluteStrokeWidth as deprecated

* chore(icons): revert git hook changes made to icons

* fix(packages/astro,react-native,vue): fix iconNode type
fix(packages/vue): fix hasA11yProp

* fix(packages/solid): make component reactive & add unit tests for checking prop changes

* test(packages/vue): add reactivity tests to @lucide/vue

* fix(packages): replace cpy-cli, accidentally removed during main merge

* docs(packages): upgrade react documentation with nonScalingStroke

* fix(tests): update snapshots for lucide-react

* feat(docs): upgraded package guides with nonScalingStroke, dropping absoluteStrokeWidth

* fix(vue,react-native): fix linting issues

* fix(icons): add proper fallback for icon width in calculatedStrokeWidth

* fix(packages/svelte): add globalProps.class to icons

* fix(packages/shared): wrap width, height & size in isDefined

* Update types.ts

* fix(packages/svelte): do not pass propsClass to mergeClasses

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
This commit is contained in:
Karsa
2026-09-04 16:38:01 +02:00
committed by GitHub
co-authored by Eric Fennis
parent 07a2c792ef
commit 99d25bdee2
178 changed files with 3257 additions and 3166 deletions
+3
View File
@@ -28,6 +28,9 @@ packages/**/src/aliases.ts
!packages/**/src/aliases/index.ts
packages/**/src/utils/*.ts
!packages/shared/src/utils/*.ts
!packages/shared/src/build/
!packages/shared/src/build/*.ts
packages/shared/src/build/types.d.ts
packages/**/src/dynamicIconImports.ts
packages/**/DynamicIcon.d.ts
packages/**/dynamicIconImports.js
+54 -46
View File
@@ -2,6 +2,7 @@
title: Combining icons - Angular
description: Learn how to combine multiple icons into a single icon nested SVG elements in your Angular application.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/SandpackAngular.vue'
</script>
@@ -14,28 +15,32 @@ This is useful for if you want to be creative and make your own custom icons by
::: sandpack {template=angular showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="@lucide/angular"}
```ts /src/app/app.component.ts [active]
import { Component, ViewEncapsulation } from "@angular/core";
import { LucideScan, LucideUser } from "@lucide/angular";
import { Component, ViewEncapsulation } from '@angular/core';
import { LucideScan, LucideUser } from '@lucide/angular';
@Component({
selector: 'app',
imports: [LucideScan, LucideUser],
template: `
<svg lucideScan [size]="48">
<svg lucideUser
[size]="12"
x="6"
y="6"
[strokeWidth]="4"
[absoluteStrokeWidth]="true"
/>
</svg>`,
template: ` <svg
lucideScan
[size]="48"
[nonScalingStroke]="true"
>
<svg
lucideUser
[size]="12"
x="6"
y="6"
[strokeWidth]="4"
[nonScalingStroke]="true"
/>
</svg>`,
styleUrls: ['./app.component.css'],
encapsulation: ViewEncapsulation.None,
})
export class App {
}
export class App {}
```
:::
This is valid SVG and all SVG properties are supported on the icons.
@@ -53,28 +58,29 @@ You can also use SVG elements to create your own icons.
For example, you can add a notification badge to an icon by using the `circle` SVG element.
::: sandpack {template=angular showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="@lucide/angular"}
```ts /src/app/app.component.ts [active]
import { Component, ViewEncapsulation, signal } from "@angular/core";
import { LucideMail } from "@lucide/angular";
import { Component, ViewEncapsulation, signal } from '@angular/core';
import { LucideMail } from '@lucide/angular';
@Component({
selector: 'app',
imports: [LucideMail],
template: `
<svg lucideMail [size]="48">
@if (hasUnreadMessages()) {
<circle
r="3"
cx="21"
cy="5"
stroke="none"
fill="#F56565"
/>
}
</svg>`,
template: ` <svg
lucideMail
[size]="48"
>
@if (hasUnreadMessages()) {
<circle
r="3"
cx="21"
cy="5"
stroke="none"
fill="#F56565"
/>
}
</svg>`,
styleUrls: ['./app.component.css'],
encapsulation: ViewEncapsulation.None,
})
@@ -82,38 +88,40 @@ export class App {
protected readonly hasUnreadMessages = signal<boolean>(true);
}
```
:::
### Example with text element
You can also use the `text` SVG element to add text to your icon.
::: sandpack {template=angular showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="@lucide/angular"}
```ts /src/app/app.component.ts [active]
import { Component, ViewEncapsulation } from "@angular/core";
import { LucideFile } from "@lucide/angular";
import { Component, ViewEncapsulation } from '@angular/core';
import { LucideFile } from '@lucide/angular';
@Component({
selector: 'app',
imports: [LucideFile],
template: `
<svg lucideFile [size]="48">
<text
x="7.5"
y="19"
font-size="8"
font-family="Verdana,sans-serif"
stroke-width="1"
>
JS
</text>
</svg>`,
template: ` <svg
lucideFile
[size]="48"
>
<text
x="7.5"
y="19"
font-size="8"
font-family="Verdana,sans-serif"
stroke-width="1"
>
JS
</text>
</svg>`,
styleUrls: ['./app.component.css'],
encapsulation: ViewEncapsulation.None,
})
export class App {
}
export class App {}
```
:::
+31 -32
View File
@@ -2,6 +2,7 @@
title: Global Styling - Angular
description: Learn how to style all icons globally in your Angular application using CSS or the provideLucideConfig provider.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/SandpackAngular.vue'
</script>
@@ -29,9 +30,9 @@ import { provideLucideConfig } from '@lucide/angular';
export const appConfig: ApplicationConfig = {
providers: [
provideLucideConfig({
strokeWidth: 1.5
strokeWidth: 1.5,
}),
]
],
};
```
@@ -65,7 +66,7 @@ All Lucide icons include the `lucide` class. You can use this class in your styl
```
```ts /src/app/app.component.ts
import { Component, ViewEncapsulation } from "@angular/core";
import { Component, ViewEncapsulation } from '@angular/core';
import {
LucideCakeSlice,
LucideCandy,
@@ -76,7 +77,7 @@ import {
LucideSandwich,
LucideWine,
LucideDessert,
} from "@lucide/angular";
} from '@lucide/angular';
@Component({
selector: 'app',
@@ -92,28 +93,27 @@ import {
LucideDessert,
],
template: `<div class="grid">
<svg lucideCakeSlice />
<svg lucideCandy />
<svg lucideApple />
<svg lucideCookie />
<svg lucideMartini />
<svg lucideIceCream2 />
<svg lucideSandwich />
<svg lucideWine />
<svg lucideDessert />
</div>`,
<svg lucideCakeSlice />
<svg lucideCandy />
<svg lucideApple />
<svg lucideCookie />
<svg lucideMartini />
<svg lucideIceCream2 />
<svg lucideSandwich />
<svg lucideWine />
<svg lucideDessert />
</div>`,
styleUrls: ['./app.component.css', './icon.css'],
encapsulation: ViewEncapsulation.None,
})
export class App {
}
export class App {}
```
:::
### Absolute stroke width
### Non-scaling strokes
To keep the stroke width constant regardless of icon size, apply `vector-effect: non-scaling-stroke` to the icon's children. See [absolute-stroke-width](../basics/stroke-width.md#absolute-stroke-width) for more details.
To keep the stroke width constant regardless of icon size, apply `vector-effect: non-scaling-stroke` to the icon's children. See [non-scaling strokes](../basics/stroke-width.md#non-scaling-strokes) for more details.
::: sandpack {template=angular editorHeight=300 dependencies="@lucide/angular"}
@@ -137,7 +137,7 @@ To keep the stroke width constant regardless of icon size, apply `vector-effect:
```
```ts /src/app/app.component.ts
import { Component, ViewEncapsulation } from "@angular/core";
import { Component, ViewEncapsulation } from '@angular/core';
import {
LucideTentTree,
LucideCaravan,
@@ -148,7 +148,7 @@ import {
LucideMap,
LucideCloudMoon,
LucideSparkles,
} from "@lucide/angular";
} from '@lucide/angular';
@Component({
selector: 'app',
@@ -164,21 +164,20 @@ import {
LucideSparkles,
],
template: `<div class="grid">
<svg lucideTentTree />
<svg lucideCaravan />
<svg lucideFlameKindling />
<svg lucideMountainSnow />
<svg lucideTrees />
<svg lucideAxe />
<svg lucideMap />
<svg lucideCloudMoon />
<svg lucideSparkles />
</div>`,
<svg lucideTentTree />
<svg lucideCaravan />
<svg lucideFlameKindling />
<svg lucideMountainSnow />
<svg lucideTrees />
<svg lucideAxe />
<svg lucideMap />
<svg lucideCloudMoon />
<svg lucideSparkles />
</div>`,
styleUrls: ['./app.component.css', './icon.css'],
encapsulation: ViewEncapsulation.None,
})
export class App {
}
export class App {}
```
:::
+23 -19
View File
@@ -1,7 +1,8 @@
---
title: Stroke width - Angular
description: Learn how to adjust the stroke width of icons in your Angular application using the `strokeWidth` input or adjust the strokeWidth appearance using the `absoluteStrokeWidth` input.
description: Learn how to adjust the stroke width of icons in your Angular application using the `strokeWidth` input or adjust the strokeWidth appearance using the `nonScalingStroke` input.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/SandpackAngular.vue'
</script>
@@ -18,55 +19,58 @@ The `strokeWidth` can be adjusted to create a different look of the icons.
::: sandpack {template=angular showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="@lucide/angular"}
```ts /src/app/app.component.ts [active]
import { Component, ViewEncapsulation } from "@angular/core";
import { LucideFolderLock } from "@lucide/angular";
import { Component, ViewEncapsulation } from '@angular/core';
import { LucideFolderLock } from '@lucide/angular';
@Component({
selector: 'app',
imports: [LucideFolderLock],
template: `<svg lucideFolderLock [strokeWidth]="1" />`,
template: `<svg
lucideFolderLock
[strokeWidth]="1"
/>`,
styleUrls: ['./app.component.css'],
encapsulation: ViewEncapsulation.None,
})
export class App {
}
export class App {}
```
:::
## Absolute stroke width
## Non-scaling strokes
When binding the `size` input, the icon's stroke width will be relative to its size, this is the default SVG behavior. The `absoluteStrokeWidth` input is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
When binding the `size` input, the icon's stroke width will be relative to its size, this is the default SVG behavior. The `nonScalingStroke` input is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
This means that when `absoluteStrokeWidth` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
This means that when `nonScalingStroke` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
Note `2px` is the default stroke width for a Lucide icon, this can be adjusted to all sizes.
![Absolute stroke width comparison](../../../images/absolute-stroke-width-compare.png?raw=true "Absolute stroke width comparison")
<!--@include: ../../../images/non-scaling-stroke-compare.svg -->
### Adjusting stroke width with `absoluteStrokeWidth` input
### Adjusting stroke width with `nonScalingStroke` input
Setting `absoluteStrokeWidth` to `true` will make the stroke width absolute.
Setting `nonScalingStroke` to `true` will make the stroke width non-scaling.
::: sandpack {template=angular showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="@lucide/angular"}
```ts /src/app/app.component.ts [active]
import { Component, ViewEncapsulation } from "@angular/core";
import { LucideRollerCoaster } from "@lucide/angular";
import { Component, ViewEncapsulation } from '@angular/core';
import { LucideRollerCoaster } from '@lucide/angular';
@Component({
selector: 'app',
imports: [LucideRollerCoaster],
template: `
<svg
<svg
lucideRollerCoaster
[size]="96"
[absoluteStrokeWidth]="true"
[nonScalingStroke]="true"
></svg>
`,
`,
styleUrls: ['./app.component.css'],
encapsulation: ViewEncapsulation.None,
})
export class App {
}
export class App {}
```
:::
+17 -10
View File
@@ -2,6 +2,7 @@
title: Getting started - Angular
description: This guide will help you get started with Lucide in your Angular project.
---
<script setup>
import OverviewLink from '../../.vitepress/theme/components/base/OverviewLink.vue'
import OverviewLinkGrid from '../../.vitepress/theme/components/base/OverviewLinkGrid.vue'
@@ -56,7 +57,7 @@ import { LucideFileText } from '@lucide/angular';
template: '<svg lucideFileText></svg>',
imports: [LucideFileText],
})
export class App { }
export class App {}
```
### Dynamic icon component
@@ -74,7 +75,7 @@ import { LucideDynamicIcon, LucideCircleCheck, LucideCircleX } from '@lucide/ang
})
export class App {
protected readonly model = signal<boolean>(true);
protected readonly icon = computed(() => this.model() ? LucideCircleCheck : LucideCircleX);
protected readonly icon = computed(() => (this.model() ? LucideCircleCheck : LucideCircleX));
}
```
@@ -82,18 +83,24 @@ export class App {
To customize the appearance of an icon, you can use the following inputs:
| name | type | default |
| ----------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `strokeWidth` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
| `title` | *string* | null |
| name | type | default |
| ------------------ | --------- | ------------ |
| `size` | _number_ | 24 |
| `color` | _string_ | currentColor |
| `strokeWidth` | _number_ | 2 |
| `nonScalingStroke` | _boolean_ | false |
| `title` | _string_ | null |
Because icons render as SVG elements, all standard SVG attributes can also be applied. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
```html
<svg lucideHouse [size]="48" color="red" [strokeWidth]="1" title="Home"></svg>
<svg
lucideHouse
[size]="48"
color="red"
[strokeWidth]="1"
title="Home"
></svg>
```
For more examples and details on how to use these inputs, continue the guide:
+2 -2
View File
@@ -27,9 +27,9 @@ Every icon has a class attribute applied called `lucide`. This class name can be
}
```
### Absolute stroke width
### Non-scaling strokes
For global absolute stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [absolute-stroke-width](../basics/stroke-width.md#absolute-stroke-width) for more info.
For global non-scaling stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [non-scaling strokes](../basics/stroke-width.md#non-scaling-strokes) for more info.
```css
.lucide {
+10 -3
View File
@@ -18,13 +18,19 @@ interface LucideProps extends SVGAttributes<SVGSVGElement> {
color?: string;
size?: number | string;
'stroke-width'?: number | string;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
[key: string]: any; // Any other SVG attributes
}
```
### Using `IconProps`
<!-- Rename this to LucideProps -->
You can use the `IconProps` interface to type props for your custom icon components.
```astro
@@ -50,7 +56,7 @@ Type for individual icon components, this is use full when you want to type a va
import type { Component } from 'astro/types';
import type { IconProps } from '@lucide/astro';
type LucideIcon = Component<IconProps>
type LucideIcon = Component<IconProps>;
```
### Using `LucideIcon`
@@ -103,12 +109,13 @@ Not commonly used directly in application code. But can be useful for advanced u
```ts
type IconNode = [
elementName: 'circle' | 'ellipse'| 'g' | 'line' | 'path' | 'polygon' | 'polyline' | 'rect',
attrs: HTMLAttributes<'svg'>
elementName: 'circle' | 'ellipse' | 'g' | 'line' | 'path' | 'polygon' | 'polyline' | 'rect',
attrs: HTMLAttributes<'svg'>,
][];
```
### Using `IconNode`
You can use the `IconNode` type when you need to work with the raw SVG structure of an icon.
```astro [CustomIcon.astro]
+8 -8
View File
@@ -1,6 +1,6 @@
---
title: Stroke width - Astro
description: Learn how to customize the stroke width of Lucide icons in your Astro applications using the strokeWidth and absoluteStrokeWidth props.
description: Learn how to customize the stroke width of Lucide icons in your Astro applications using the strokeWidth and nonScalingStroke props.
---
# Stroke width
@@ -20,19 +20,19 @@ import FolderLock from '@lucide/astro/icons/folder-lock';
<FolderLock strokeWidth={1} />
```
## Absolute stroke width
## Non-scaling strokes
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `absoluteStrokeWidth` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `nonScalingStroke` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
This means that when `absoluteStrokeWidth` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
This means that when `nonScalingStroke` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
Note `2px` is the default stroke width for a Lucide icon, this can be adjusted to all sizes.
![Absolute stroke width comparison](../../../images/absolute-stroke-width-compare.png?raw=true "Absolute stroke width comparison")
<!--@include: ../../../images/non-scaling-stroke-compare.svg -->
### Adjusting stroke width with `absoluteStrokeWidth` prop
### Adjusting stroke width with `nonScalingStroke` prop
Setting `absoluteStrokeWidth` to `true` will make the stroke width absolute.
Setting `nonScalingStroke` to `true` will make the stroke width non-scaling.
```astro
---
@@ -41,6 +41,6 @@ import RollerCoaster from '@lucide/astro/icons/roller-coaster';
<RollerCoaster
size={96}
absoluteStrokeWidth
nonScalingStroke
/>
```
+7 -7
View File
@@ -54,13 +54,13 @@ import { Camera } from '@lucide/astro';
To customize the appearance of an icon, you can use the following props:
| name | type | default |
| ----------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `stroke-width` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
| `default-class` | *string* | lucide-icon |
| name | type | default |
| ------------------ | --------- | ------------ |
| `size` | _number_ | 24 |
| `color` | _string_ | currentColor |
| `stroke-width` | _number_ | 2 |
| `nonScalingStroke` | _boolean_ | false |
| `default-class` | _string_ | lucide-icon |
Because icons render as SVG elements, all standard SVG attributes can also be applied as props. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
@@ -2,8 +2,7 @@
`@lucide/icons` is a helper library that exports Lucide **icon data** in a tree-shakable format, also providing utilities for dynamic importing icons.
It intentionally ships **no real rendering logic or components** — other packages (for example [`@lucide/angular`](http://npmjs.com/package/@lucide/angular)) can consume this data to render icons in their respective
frameworks. You can also use this package to build third-party integrations for frameworks we don't (yet) support.
It intentionally ships **no real rendering logic or components** — but you can use this package to build third-party integrations for frameworks we don't (yet) support.
## Installation
@@ -35,14 +34,11 @@ Each icon is described by the following interface:
export type LucideIconData = {
name: string;
node: LucideIconNode[];
} & (
| { size: number }
| { width: number; height: number; }
);
} & ({ size: number } | { width: number; height: number });
```
| name | type | description |
|------------------------------|--------------------|--------------------------------------------------------------------|
| ---------------------------- | ------------------ | ------------------------------------------------------------------ |
| `name` | `string` | The name of the icon. |
| `node` | `LucideIconNode[]` | SVG child nodes as `[tagName, attributes]` tuples. |
| `size` or `width` & `height` | `number` | The dimensions of the icon (`size` is shorthand for square icons). |
@@ -65,16 +61,16 @@ All builders accept the same `params` object (`LucideBuildParams`) to customize
The following parameters are supported (names reflect the current implementation):
| param | type | effect |
|-----------------------|--------------------------|------------------------------------------------------------------------------------|
| `color` | `string` | Sets `stroke` (defaults to `currentColor`). |
| `size` | `number` | Sets both `width` and `height` (defaults to 24). |
| `width` | `number` | Sets `width` only. |
| `height` | `number` | Sets `height` only. |
| `strokeWidth` | `number` | Sets `stroke-width` (defaults to 2). |
| `absoluteStrokeWidth` | `boolean` | Adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements. |
| `className` | `string` | Appended to the generated `class` attribute. |
| `attributes` | `Record<string, string>` | Add or override any generated SVG attributes (including `class`, `viewBox`, etc.). |
| param | type | effect |
| ------------------ | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `color` | `string` | Sets `stroke` (defaults to `currentColor`). |
| `size` | `number` | Sets both `width` and `height` (defaults to 24). |
| `width` | `number` | Sets `width` only. |
| `height` | `number` | Sets `height` only. |
| `strokeWidth` | `number` | Sets `stroke-width` (defaults to 2). |
| `nonScalingStroke` | `boolean` | Adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements. |
| `className` | `string` | Appended to the generated `class` attribute. |
| `attributes` | `Record<string, string>` | Add or override any generated SVG attributes (including `class`, `viewBox`, and so on). |
::: info
SVG attributes generated by the builders include a default Lucide setup (`xmlns`, `viewBox`, `fill="none"`, `stroke="currentColor"`, `stroke-width="2"`, `stroke-linecap="round"`, `stroke-linejoin="round"`), plus a class string of the form: `lucide lucide-{iconName}`.
@@ -127,6 +123,7 @@ document.body.appendChild(el);
Creates a base64-encoded SVG data URI from a Lucide icon object.
This helper works in both browsers and Node.js:
- In browsers it uses `btoa` (with proper UTF-8 handling)
- In Node.js it falls back to `Buffer`
@@ -138,15 +135,18 @@ const uri = buildLucideDataUri(House, { size: 24 });
```
The returned value can be used directly in places such as:
- `<img src="...">`
- CSS `background-image`
- Canvas drawing
- Inline data URLs in HTML or SVG
::: tip Environment notes
- The SVG is encoded as UTF-8 before base64 conversion to ensure correct handling of non-ASCII characters.
- No runtime configuration is required — the function automatically selects the appropriate encoding strategy.
- If neither `btoa` nor `Buffer` is available, an error is thrown.
The SVG is encoded as UTF-8 before base64 conversion to ensure correct handling of non-ASCII characters.
No runtime configuration is required. The function automatically selects the appropriate encoding strategy.
If neither `btoa` nor `Buffer` is available, an error is thrown.
:::
## Dynamic imports
@@ -156,6 +156,7 @@ Dynamic imports are useful when you only know the icon name at runtime (for exam
::: tip
Validate `iconName` before indexing the map (and provide a fallback icon) to avoid runtime errors.
:::
## Dynamic imports
Dynamic imports are useful when the icon name is only known at runtime (for example, icon names stored in a CMS or database). For purely static usage, prefer direct imports for maximum tree-shaking.
+16 -14
View File
@@ -1,7 +1,8 @@
---
title: Stroke width - Lucide
description: Learn how to customize the stroke width of Lucide icons in your Vanilla JavaScript applications using the strokeWidth and absoluteStrokeWidth attributes.
description: Learn how to customize the stroke width of Lucide icons in your Vanilla JavaScript applications using the strokeWidth and nonScalingStroke attributes.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/Sandpack.vue'
</script>
@@ -18,10 +19,13 @@ The `strokeWidth` can be adjusted to create a different look of the icons.
::: sandpack {template=vanilla showTabs=false editorHeight=250 editorWidthPercentage=70 dependencies="lucide"}
```html /index.html [active]
<!DOCTYPE html>
<!doctype html>
<html>
<body>
<i data-lucide="folder-lock" stroke-width="1"></i>
<i
data-lucide="folder-lock"
stroke-width="1"
></i>
<script src="index.js"></script>
</body>
@@ -29,34 +33,32 @@ The `strokeWidth` can be adjusted to create a different look of the icons.
```
```js /index.js
import "./styles.css";
import './styles.css';
import { createIcons, FolderLock } from 'lucide/dist/cjs/lucide';
createIcons({
icons: {
FolderLock,
}
},
});
```
:::
<!-- ## Non-scaling strokes
<!-- ## Absolute stroke width
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `nonScalingStroke` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `absoluteStrokeWidth` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
This means that when `absoluteStrokeWidth` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
This means that when `nonScalingStroke` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
Note `2px` is the default stroke width for a Lucide icon, this can be adjusted to all sizes.
![Absolute stroke width comparison](../../../images/absolute-stroke-width-compare.png?raw=true "Absolute stroke width comparison")
[Non-scaling stroke comparison](../../../images/non-scaling-stroke-compare.svg)
### Adjusting stroke width with `absoluteStrokeWidth` prop
### Adjusting stroke width with `nonScalingStroke` prop
Setting `absoluteStrokeWidth` to `true` will make the stroke width absolute.
Setting `nonScalingStroke` to `true` will make the stroke width non-scaling.
::: sandpack {template=vanilla showTabs=false editorHeight=250 editorWidthPercentage=70 dependencies="lucide"}
@@ -64,7 +66,7 @@ Setting `absoluteStrokeWidth` to `true` will make the stroke width absolute.
<!DOCTYPE html>
<html>
<body>
<i data-lucide="roller-coaster" stroke-width="96" absolute-stroke-width="true"></i>
<i data-lucide="roller-coaster" stroke-width="96" non-scaling-stroke="true"></i>
<script src="index.js"></script>
</body>
-286
View File
@@ -1,286 +0,0 @@
# `@lucide/angular`
::: warning
This documentation is for `@lucide/angular`.
To learn about our legacy package for Angular, please refer to [`lucide-angular`](https://v0.lucide.dev/guide/packages/lucide-angular).
:::
A standalone, signal-based, zoneless implementation of Lucide icons for Angular.
**What you can accomplish:**
- Use icons as standalone Angular components with full dependency injection support
- Configure icons globally through modern Angular providers
- Integrate with Angular's reactive forms and data binding
- Build scalable applications with tree-shaken icons and lazy loading support
## Prerequisites
This package requires Angular 17+ and uses standalone components, signals, and zoneless change detection.
## Installation
::: code-group
```sh [pnpm]
pnpm add @lucide/angular
```
```sh [yarn]
yarn add @lucide/angular
```
```sh [npm]
npm install @lucide/angular
```
```sh [bun]
bun add @lucide/angular
```
:::
## How to use
### Standalone icons
Every icon can be imported as a ready-to-use standalone component:
```html
<svg lucideFileText></svg>
```
```ts{2,7}
import { Component } from '@angular/core';
import { LucideFileText } from '@lucide/angular';
@Component({
selector: 'app-foobar',
templateUrl: './foobar.html',
imports: [LucideFileText],
})
export class Foobar { }
```
::: tip
Standalone icon components use the selector `svg[lucide{PascalCaseIconName}]`.
This ensures minimal bloating of the DOM and the ability to directly manipulate all attributes of the resulting SVG element.
:::
### Dynamic icon component
You may also use the dynamic `LucideIcon` component to dynamically render icons.
#### With tree-shaken imports
You may pass imported icons directly to the component:
```html{3}
@for (item of items) {
<a navbarItem [routerLink]="item.routerLink">
<svg [lucideIcon]="item.icon"></svg>
{{ item.title }}
</a>
}
```
```ts{2,8,14,19}
import { Component } from '@angular/core';
import { LucideIcon, LucideHouse, LucideUsersRound } from '@lucide/angular';
import { NavbarItem, NavbarItemModel } from './navbar-item';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.html',
imports: [LucideIcon, NavbarItem],
})
export class Navbar {
readonly items: NavbarItemModel[] = [
{
title: 'Home',
icon: LucideHouse,
routerLink: [''],
},
{
title: 'Users',
icon: LucideUsersRound,
routerLink: ['admin/users'],
},
];
}
```
#### With icons provided via dependency injection
Alternatively, the component also accepts string inputs.
To use icons this way, first, you have to provide icons via `provideLucideIcons`:
:::code-group
```ts{7-10} [app.config.ts]
import { ApplicationConfig } from '@angular/core';
import { provideLucideIcons, LucideCircleCheck, LucideCircleX } from '@lucide/angular';
export const appConfig: ApplicationConfig = {
providers: [
// ...
provideLucideIcons(
LucideCircleCheck,
LucideCircleX,
),
]
};
```
```html [foobar.html]
<svg lucideIcon="circle-check"></svg>
```
```ts{7} [foobar.ts]
import { Component } from '@angular/core';
import { LucideIcon } from '@lucide/angular';
@Component({
selector: 'app-foobar',
templateUrl: './template-url',
imports: [LucideIcon],
})
export class Foobar { }
```
:::
::: tip
For optimal bundle size, provide icons at the highest appropriate level in your application.
Providing all icons at the root level may increase your initial bundle size, while providing them at feature module level enables better code splitting.
:::
::: warning
While you may provide your icons at any level of the dependency injection tree, be aware that [Angular's DI system is hierarchical](https://angular.dev/guide/di/defining-dependency-providers#injector-hierarchy-in-angular): `LucideIcon` will only have access to the icons provided closest to it in the tree.
:::
## Accessible labels
You can use the `title` input property to set the [accessible name element](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/title) on the SVG:
```html
<svg lucideIcon="house" title="Go to dashboard"></svg>
```
This will result in the following output:
```html{2}
<svg class="lucide lucide-house" ...>
<title>Go to dashboard</title>
<!-- SVG paths -->
</svg>
```
## Props
You can pass additional props to adjust the icon appearance.
| name | type | default |
|-----------------------|-----------|--------------|
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `strokeWidth` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
```html
<svg lucideHouse size="48" color="red" strokeWidth="1"></svg>
```
## Global configuration
You can use `provideLucideConfig` to configure the default property values as defined above:
```ts{2,7-9}
import { ApplicationConfig } from '@angular/core';
import { provideLucideConfig } from '@lucide/angular';
export const appConfig: ApplicationConfig = {
providers: [
// ...
provideLucideConfig({
strokeWidth: 1.5
}),
]
};
```
## Styling via CSS
Icons can also be styled by using custom CSS classes:
```html
<svg lucideHousePlus class="my-icon"></svg>
```
```css
svg.my-icon {
width: 12px;
height: 12px;
stroke-width: 3;
}
```
## With Lucide lab or custom icons
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
While they aren't provided as standalone components, they can be still be passed to the `LucideIcon` component the same way as official icons:
```html
<!-- Directly as LucideIconData: -->
<svg [lucideIcon]="CoconutIcon"></svg>
<!-- As a provided icon by name: -->
<svg lucideIcon="coconut"></svg>
```
```ts{2,6-7,11-12}
import { provideLucideIcons } from '@lucide/angular';
import { coconut } from '@lucide/lab';
@Component({
templateUrl: './foobar.html',
// For using by name via provider:
providers: [provideLucideIcons({ coconut })],
imports: [LucideIcon]
})
export class Foobar {
// For passing directly as LucideIconData:
readonly CoconutIcon = coconut;
}
```
## Troubleshooting
### The icon is not being displayed
If using per-icon-components:
1. Ensure that the icon component is being imported, if using per-icon-components
2. Check that the icon name matches exactly (case-sensitive)
If using the dynamic component:
1. Ensure the icon is provided via `provideLucideIcons()` if using string names
2. Verify the icon is imported from `@lucide/angular` and not the legacy package
### TypeScript errors?
Make sure you're importing from `@lucide/angular` and not `lucide-angular`.
### Icons render with wrong defaults
Ensure `provideLucideConfig()` is used at the right level.
## Migration guide
Migrating from `lucide-angular`? Read our [comprehensive migration guide](https://github.com/lucide-icons/lucide/blob/main/packages/angular/MIGRATION.md).
-210
View File
@@ -1,210 +0,0 @@
# Lucide Astro
Astro components for Lucide icons that work perfectly with Astro's island architecture and multi-framework support. Each icon is an Astro component that renders as an inline SVG, providing excellent performance for static sites and server-side rendering scenarios.
**What you can accomplish:**
- Use icons as Astro components with zero JavaScript runtime overhead
- Build fast, static websites with optimized SVG icons
- Integrate seamlessly with Astro's component islands and partial hydration
- Create multi-framework applications where icons work across different UI libraries
- Optimize performance with direct icon imports and build-time rendering
## Installation
::: code-group
```sh [pnpm]
pnpm add @lucide/astro
```
```sh [yarn]
yarn add @lucide/astro
```
```sh [npm]
npm install @lucide/astro
```
```sh [bun]
bun add @lucide/astro
```
:::
## How to use
Lucide is built with ES Modules, so it's completely tree-shakable.
Each icon can be imported as an Astro component, which renders an inline SVG element. This way, only the icons that are imported into your project are included in the final bundle. The rest of the icons are tree-shaken away.
### Example
Default usage:
```astro
---
import { Skull } from '@lucide/astro';
---
<Skull />
```
Additional props can be passed to adjust the icon:
```astro
---
import { Camera } from '@lucide/astro';
---
<Camera color="#ff3e98" />
```
For faster builds and load times, you can import icons directly from the `@lucide/astro/icons` directory:
```astro
---
import CircleAlert from '@lucide/astro/icons/circle-alert';
---
<CircleAlert color="#ff3e98" />
```
## Props
| name | type | default |
| --------------------- | --------- | ------------ |
| `size` | _number_ | 24 |
| `color` | _string_ | currentColor |
| `stroke-width` | _number_ | 2 |
| `absoluteStrokeWidth` | _boolean_ | false |
### Applying props
To customize the appearance of an icon, you can pass custom properties as props directly to the component. The component accepts all SVG attributes as props, which allows flexible styling of the SVG elements. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
```astro
---
import { Phone } from '@lucide/astro';
---
<Phone fill="#333" />
```
This results a filled phone icon.
## Types
The package includes type definitions for all icons. This is useful if you want to dynamically render icons.
### Example
```astro
---
import { House, Library, Cog, type Icon as IconType } from '@lucide/astro';
type MenuItem = {
name: string;
href: string;
icon: typeof IconType;
};
const menuItems: MenuItem[] = [
{
name: 'Home',
href: '/',
icon: House,
},
{
name: 'Blog',
href: '/blog',
icon: Library,
},
{
name: 'Projects',
href: '/projects',
icon: Cog,
},
];
---
{
menuItems.map((item) => (
<a href={item.href}>
<item.icon />
<span>{item.name}</span>
</a>
))
}
```
## With Lucide Lab or custom icons
[Lucide Lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
They can be used by using the `Icon` component.
All props of the regular Lucide icons can be passed to adjust the icon appearance.
### Using the `Icon` component
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
```astro
---
import { Icon } from '@lucide/astro';
import { burger, sausage } from '@lucide/lab';
---
<Icon iconNode={burger} />
<Icon iconNode={sausage} color="red"/>
```
## One generic icon component
It is possible to create one generic icon component to load icons, but it is not recommended.
::: danger
The example below imports all ES Modules, so exercise caution when using it. Importing all icons will significantly increase the build size of the application. This may be passable if you're doing SSG and SSR in server environments. However if you're doing SSR in serverless environments, it could negatively affect your app's performance, as a bigger bundle size will translate to an increase in cold starts.
:::
### Icon Component Example
```astro
---
import { icons, type IconProps } from '@lucide/astro';
interface Props extends IconProps {
name: keyof typeof icons;
}
const { name, ...restProps } = Astro.props;
const Icon = icons[name];
---
<Icon {...restProps} />
```
### Using the Icon Component
```astro
---
import LucideIcon from './LucideIcon.astro';
---
<LucideIcon name="Menu" />
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```jsx
---
import { Check } from '@lucide/astro';
---
<Check aria-label="Task completed" />
```
For best practices on accessibility, please see our [accessibility guide](../accessibility.md).
-149
View File
@@ -1,149 +0,0 @@
---
title: Lucide Preact
---
# Lucide Preact
Preact components for Lucide icons that provide React-like development experience with a smaller footprint. Each icon is a lightweight Preact component that renders as an inline SVG, perfect for applications that need React compatibility with minimal bundle size.
**What you can accomplish:**
- Use icons as Preact components with React-like syntax and patterns
- Build lightweight applications with Preact's smaller runtime
- Create fast, responsive interfaces with minimal JavaScript overhead
- Maintain React compatibility while reducing bundle size
- Integrate with existing Preact applications and component libraries
## Installation
::: code-group
```sh [pnpm]
pnpm add lucide-preact
```
```sh [yarn]
yarn add lucide-preact
```
```sh [npm]
npm install lucide-preact
```
```sh [bun]
bun add lucide-preact
```
:::
## How to use
Lucide is built with ES Modules, so it's completely tree-shakable.
Each icon can be imported as a Preact component, which renders an inline SVG element. This way, only the icons that are imported into your project are included in the final bundle. The rest of the icons are tree-shaken away.
### Example
Additional props can be passed to adjust the icon:
```jsx
import { Camera } from 'lucide-preact';
// Usage
const App = () => {
return <Camera color="red" size={48} />;
};
export default App;
```
## Props
| name | type | default |
| --------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `strokeWidth` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
### Applying props
To customize the appearance of an icon, you can pass custom properties as props directly to the component. The component accepts all SVG attributes as props, which allows flexible styling of the SVG elements. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
```jsx
// Usage
const App = () => {
return <Camera fill="red" stroke-linejoin="bevel" />;
};
```
> SVG attributes in Preact aren't transformed, so if you want to change for example the `stroke-linejoin` you need to pass it in kebabcase. Basically how the SVG spec want you to write it. See this topic in the [Preact documentation](https://preactjs.com/guide/v10/differences-to-react/#svg-inside-jsx).
## With Lucide Lab or custom icons
[Lucide Lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
They can be used by using the `Icon` component.
All props like regular Lucide icons can be passed to adjust the icon appearance.
### Using the `Icon` component
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
```jsx
import { Icon } from 'lucide-preact';
import { coconut } from '@lucide/lab';
const App = () => (
<Icon iconNode={coconut} />
);
```
## One generic icon component
It is possible to create one generic icon component to load icons, but it is not recommended.
::: danger
The example below imports all ES Modules, so exercise caution when using it. Importing all icons will significantly increase the build size of the application, negatively affecting its performance. This is especially important when using bundlers like `Webpack`, `Rollup`, or `Vite`.
:::
### Icon Component Example
```jsx
import { icons } from 'lucide-preact';
const Icon = ({ name, color, size }) => {
const LucideIcon = icons[name];
return <LucideIcon color={color} size={size} />;
};
export default Icon;
```
#### Using the Icon Component
```jsx
import Icon from './Icon';
const App = () => {
return <Icon name="house" />;
};
export default App;
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```jsx
import { Check } from 'lucide-preact';
const App = () => {
return <Check aria-label="Task completed" />;
};
```
For best practices on accessibility, please see our [accessibility guide](../accessibility.md).
-133
View File
@@ -1,133 +0,0 @@
# Lucide React Native
React Native components for Lucide icons that work seamlessly across iOS and Android platforms. Built on top of react-native-svg, each icon renders as a native SVG component, providing consistent visual appearance and performance across mobile devices.
**What you can accomplish:**
- Use icons as React Native components with platform-consistent rendering
- Build cross-platform mobile apps with scalable vector icons
- Create responsive interfaces that adapt to different screen densities
- Integrate with React Native's styling system and animation libraries
- Maintain consistent icon appearance across iOS and Android platforms
## Installation
First, ensure that you have `react-native-svg` (version between 12 and 15) installed. Then, install the package:
::: code-group
```sh [pnpm]
pnpm add lucide-react-native
```
```sh [yarn]
yarn add lucide-react-native
```
```sh [npm]
npm install lucide-react-native
```
```sh [bun]
bun add lucide-react-native
```
:::
## How to use
Each icon can be imported as a React component.
### Example
Additional props can be passed to adjust the icon:
```jsx
import { Camera } from 'lucide-react-native';
// Usage
const App = () => {
return <Camera color="red" size={48} />;
};
export default App;
```
## Props
| name | type | default |
| --------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `strokeWidth` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
### Applying props
To customize the appearance of an icon, you can pass custom properties as props directly to the component. The component accepts all SVG attributes as props, which allows flexible styling of the SVG elements.
```jsx
// Usage
const App = () => {
return <Camera fill="red" />;
};
```
## With Lucide Lab or custom icons
[Lucide Lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
They can be used by using the `Icon` component.
All props like regular Lucide icons can be passed to adjust the icon appearance.
### Using the `Icon` component
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
```jsx
import { Icon } from 'lucide-react-native';
import { coconut } from '@lucide/lab';
const App = () => (
<Icon iconNode={coconut} />
);
```
## One generic icon component
It is possible to create one generic icon component to load icons, but it is not recommended.
::: warning
The example below imports all ES Modules, so exercise caution when using it. Importing all icons will significantly increase the build size of the application, negatively affecting its performance. This is especially important to keep in mind when using bundlers like `Webpack`, `Rollup`, or `Vite`.
:::
### Icon Component Example
```tsx
import * as icons from 'lucide-react-native/icons';
interface IconProps {
name: keyof typeof icons;
color?: string;
size?: number;
}
const Icon = ({ name, color, size }: IconProps) => {
const LucideIcon = icons[name];
return <LucideIcon color={color} size={size} />;
};
export default Icon;
```
#### Using the Icon Component
```tsx
import Icon from './Icon';
const App = () => {
return <Icon name="House" />;
};
export default App;
```
-162
View File
@@ -1,162 +0,0 @@
# Lucide Solid
SolidJS components for Lucide icons that leverage Solid's fine-grained reactivity system. Each icon is a reactive Solid component that renders as an inline SVG, providing exceptional performance through Solid's compile-time optimizations and reactive primitives.
**What you can accomplish:**
- Use icons as SolidJS components with fine-grained reactivity
- Create highly performant interfaces with Solid's reactive system
- Build dynamic icon components that respond to signals and stores
- Integrate seamlessly with Solid's JSX and component patterns
- Optimize performance with direct icon imports and minimal runtime overhead
## Installation
::: code-group
```sh [pnpm]
pnpm add lucide-solid
```
```sh [yarn]
yarn add lucide-solid
```
```sh [npm]
npm install lucide-solid
```
```sh [bun]
bun add lucide-solid
```
:::
## How to use
Lucide is built with ES Modules, so it's completely tree-shakable.
Each icon can be imported as a Solid component, which renders an inline SVG element. This way, only the icons that are imported into your project are included in the final bundle. The rest of the icons are tree-shaken away.
### Example
Additional props can be passed to adjust the icon:
```jsx
import { Camera } from 'lucide-solid';
// Usage
const App = () => {
return <Camera color="red" size={48} />;
};
export default App;
```
Vite loading/performing issues with the dev server can be resolved by import icons directly from the `lucide-solid/icons` directory:
```jsx
import Camera from 'lucide-solid/icons/camera';
// Usage
const App = () => {
return <Camera color="red" size={48} />;
};
export default App;
```
## Props
| name | type | default |
| --------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `strokeWidth` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
### Applying props
To customize the appearance of an icon, you can pass custom properties as props directly to the component. The component accepts all SVG attributes as props, which allows flexible styling of the SVG elements. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
```jsx
// Usage
const App = () => {
return <Camera fill="red" stroke-linejoin="bevel" />;
};
```
## With Lucide Lab or custom icons
[Lucide Lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
They can be used by using the `Icon` component.
All props like the regular Lucide icons can be passed to adjust the icon appearance.
### Using the `Icon` component
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
```jsx
import { Icon } from 'lucide-solid';
import { sausage } from '@lucide/lab';
const App = () => (
<Icon iconNode={sausage} color="red"/>
);
```
## One generic icon component
It is possible to create one generic icon component to load icons. It's not recommended.
::: danger
The example below imports all ES Modules, so exercise caution when using it. Importing all icons will significantly increase the build size of the application, negatively affecting its performance. This is especially important to keep in mind when using bundlers like `Webpack`, `Rollup`, or `Vite`.
:::
### Icon Component Example
```tsx
import { icons, type LucideProps } from 'lucide-solid';
import { splitProps } from 'solid-js';
import { Dynamic } from 'solid-js/web';
interface IconProps extends LucideProps {
name: keyof typeof icons;
}
const Icon = (props: IconProps) => {
const [local, others] = splitProps(props, ["name"]);
return <Dynamic component={icons[local.name]} {...others} />
};
export default Icon;
```
#### Using the Icon Component
```tsx
import Icon from './Icon';
const App = () => {
return <Icon name="home" />;
};
export default App;
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```jsx
import { Check } from 'lucide-solid';
const App = () => {
return <Check aria-label="Task completed" />;
};
```
For best practices on accessibility, please see our [accessibility guide](../accessibility.md).
-265
View File
@@ -1,265 +0,0 @@
# Lucide Static
Static assets and utilities for Lucide icons that work without JavaScript frameworks. This package provides multiple formats including individual SVG files, SVG sprites, icon fonts, and Node.js utilities for server-side rendering and static site generation.
**What you can accomplish:**
- Use individual SVG files as images or CSS background images
- Implement icon fonts for CSS-based icon systems
- Create SVG sprites for efficient icon loading in static sites
- Import SVG strings in Node.js applications and server-side rendering
- Build static websites and applications without JavaScript framework dependencies
This package includes the following implementations of Lucide icons:
- Individual SVG files
- SVG sprite
- Icon font files
- A JavaScript library exporting SVG strings
## Who is this for?
`lucide-static` is suitable for _very specific use cases_ where you want to use Lucide icons without relying on a JavaScript framework or component system. It's ideal for:
- Projects that use icon fonts with plain CSS or utility-first frameworks
- Embedding raw SVG files or sprites directly in HTML
- Using SVGs as CSS background images
- Importing SVG strings into Node.js (CommonJS) environments
::: danger
### Not recommended for production {#production-warning}
SVG sprites and icon fonts include **all icons**, which can significantly increase your app's bundle size and load time.
For production environments, we recommend using a bundler with tree-shaking support to include only the icons you actually use. Consider using one of the framework-specific [packages](../../packages.md).
:::
## Installation
::: code-group
```sh [pnpm]
pnpm add lucide-static
```
```sh [yarn]
yarn add lucide-static
```
```sh [npm]
npm install lucide-static
```
```sh [bun]
bun add lucide-static
```
:::
## SVG Files
You can use standalone SVG files or SVG sprites in several ways.
Check out our [codesandbox example](https://codesandbox.io/s/using-the-svg-sprite-lz1kk).
### SVG file as image
#### In HTML:
::: code-group
```html [Webpack]
<!-- SVG file for a single icon -->
<img src="~lucide-static/icons/house.svg" />
```
```html [CDN]
<!-- SVG file for a single icon -->
<img src="https://unpkg.com/lucide-static@latest/icons/house.svg" />
```
:::
#### In CSS:
::: code-group
```css [Webpack]
.house-icon {
background-image: url(~lucide-static/icons/house.svg);
}
```
```css [CDN]
.house-icon {
background-image: url(https://unpkg.com/lucide-static@latest/icons/house.svg);
}
```
:::
Make sure you have the correct Webpack loader configured, such as [`url-loader`](https://v4.webpack.js.org/loaders/url-loader/).
### SVG file as string
To import an SVG as a string (e.g., for templating):
::: code-group
```js [Webpack]
import arrowRightIcon from 'lucide-static/icons/arrow-right';
```
```js [Vite]
import arrowRightIcon from 'lucide-static/icons/arrow-right.svg?raw';
```
:::
You'll need an SVG loader like [`svg-inline-loader`](https://v4.webpack.js.org/loaders/svg-inline-loader/).
### Using the SVG sprite
:::danger
[Not intended for production use.](#production-warning)
:::
You may also need an additional SVG loader to handle this.
#### Basic sprite usage (not production-optimized):
```html
<img src="lucide-static/sprite.svg#house" />
```
#### Inline usage:
```html
<svg
width="24"
height="24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<use href="#alert-triangle" />
</svg>
<!-- sprite SVG -->
<svg>...</svg>
```
#### Inline with CSS helper class
If you'd prefer, you can use CSS to hold your base SVG properties:
```css
.lucide-icon {
width: 24px;
height: 24px;
stroke: currentColor;
fill: none;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
```
...and update the SVG as follows:
```xml
<svg
xmlns="http://www.w3.org/2000/svg"
class="lucide-icon"
>
<use href="#triangle-alert" />
</svg>
<!-- sprite SVG -->
<svg>...</svg>
```
## Icon font
:::danger
[Not intended for production use.](#production-warning)
:::
Lucide icons are also available as a web font. To use them, you first need to include the corresponding stylesheet:
::: code-group
```css [Vite]
@import 'lucide-static/font/lucide.css';
```
```css [Webpack]
@import ('~lucide-static/font/lucide.css');
```
```html [CDN]
<link rel="stylesheet" href="https://unpkg.com/lucide-static@latest/font/lucide.css" />
```
```html [Static asset]
<link rel="stylesheet" href="/your/path/to/lucide.css" />
```
:::
Once included, use the format `icon-{kebab-case-name}`. You can copy icon names from the [Lucide Icons page](https://lucide.dev/icons).
```html
<div class="icon-house"></div>
```
If you're not using a package manager, you can download the font files directly from the [latest release](https://github.com/lucide-icons/lucide/releases/latest).
## Node.js
You can also import Lucide icons in Node.js projects:
::: code-group
```js [ESM]
import {MessageSquare} from 'lucide-static';
```
```js [CommonJs]
const {MessageSquare} = require('lucide-static');
```
:::
> Note: Each icon name is in PascalCase.
#### Express app example in Node.js
```js
import express from 'express';
import {MessageSquare} from 'lucide-static';
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<h1>Lucide Icons</h1>
<p>This is a Lucide icon ${MessageSquare}</p>
</body>
</html>
`);
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
```
-349
View File
@@ -1,349 +0,0 @@
# Lucide Svelte
Svelte components for Lucide icons that work seamlessly with both Svelte 4 and Svelte 5. Each icon is a reactive Svelte component that renders as an inline SVG, providing excellent performance and integration with Svelte's reactive system and modern features.
**What you can accomplish:**
- Use icons as Svelte components with full reactivity and TypeScript support
- Bind icon properties to reactive variables and stores
- Create dynamic icon systems that respond to application state
- Build type-safe interfaces with comprehensive TypeScript definitions
- Optimize bundle sizes with direct icon imports and tree-shaking
## Installation
::: code-group
```sh [pnpm]
pnpm add @lucide/svelte
```
```sh [yarn]
yarn add @lucide/svelte
```
```sh [npm]
npm install @lucide/svelte
```
```sh [bun]
bun add @lucide/svelte
```
:::
> `@lucide/svelte` is only for Svelte 5, for Svelte 4 use the `lucide-svelte` package.
## How to use
Lucide is built with ES Modules, so it's completely tree-shakable.
Each icon can be imported as a Svelte component, which renders an inline SVG element. This way, only the icons that are imported into your project are included in the final bundle. The rest of the icons are tree-shaken away.
### Example
Default usage:
```svelte
<script>
import { Skull } from '@lucide/svelte';
</script>
<Skull />
```
Additional props can be passed to adjust the icon:
```svelte
<script>
import { Camera } from '@lucide/svelte';
</script>
<Camera color="#ff3e98" />
```
For faster builds and load times, you can import icons directly from the `@lucide/svelte/icons` directory:
```svelte
<script>
import CircleAlert from '@lucide/svelte/icons/circle-alert';
</script>
<CircleAlert color="#ff3e98" />
```
## Props
| name | type | default |
| --------------------- | --------- | ------------ |
| `size` | _number_ | 24 |
| `color` | _string_ | currentColor |
| `strokeWidth` | _number_ | 2 |
| `absoluteStrokeWidth` | _boolean_ | false |
### Applying props
To customize the appearance of an icon, you can pass custom properties as props directly to the component. The component accepts all SVG attributes as props, which allows flexible styling of the SVG elements. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
```svelte
<script>
import { Phone } from '@lucide/svelte';
</script>
<Phone fill="#333" />
```
This results a filled phone icon.
## Types
The package includes type definitions for all icons. This is useful if you want to dynamically load icons with the `svelte:component` directive whether you are using TypeScript or JSDoc.
### TypeScript Example
::: code-group
```svelte [Svelte 5]
<script lang="ts">
import { Home, Library, Cog, type Icon as IconType } from '@lucide/svelte';
type MenuItem = {
name: string;
href: string;
icon: typeof IconType;
};
const menuItems: MenuItem[] = [
{
name: 'Home',
href: '/',
icon: Home
},
{
name: 'Blog',
href: '/blog',
icon: Library
},
{
name: 'Projects',
href: '/projects',
icon: Cog
}
];
</script>
{#each menuItems as item}
{@const Icon = item.icon}
<a href={item.href}>
<Icon />
<span>{item.name}</span>
</a>
{/each}
```
```svelte [Svelte 4]
<script lang="ts">
import { Home, Library, Cog, type Icon } from '@lucide/svelte';
import type { ComponentType } from 'svelte';
type MenuItem = {
name: string;
href: string;
icon: ComponentType<Icon>;
};
const menuItems: MenuItem[] = [
{
name: 'Home',
href: '/',
icon: Home
},
{
name: 'Blog',
href: '/blog',
icon: Library
},
{
name: 'Projects',
href: '/projects',
icon: Cog
}
];
</script>
{#each menuItems as item}
{@const Icon = item.icon}
<a href={item.href}>
<Icon />
<span>{item.name}</span>
</a>
{/each}
```
:::
### JSDoc Example
::: code-group
```svelte [Svelte 5]
<script>
import { Home, Library, Cog } from '@lucide/svelte';
/**
* @typedef {Object} MenuItem
* @property {string} name
* @property {string} href
* @property {typeof import('@lucide/svelte').Icon} icon
*/
/** @type {MenuItem[]} */
const menuItems = [
{
name: 'Home',
href: '/',
icon: Home
},
{
name: 'Blog',
href: '/blog',
icon: Library
},
{
name: 'Projects',
href: '/projects',
icon: Cog
}
];
</script>
{#each menuItems as item}
{@const Icon = item.icon}
<a href={item.href}>
<Icon />
<span>{item.name}</span>
</a>
{/each}
```
```svelte [Svelte 4]
<script>
import { Home, Library, Cog } from '@lucide/svelte';
/**
* @typedef {Object} MenuItem
* @property {string} name
* @property {string} href
* @property {import('svelte').ComponentType<import('@lucide/svelte').Icon>} icon
*/
/** @type {MenuItem[]} */
const menuItems = [
{
name: 'Home',
href: '/',
icon: Home,
},
{
name: 'Blog',
href: '/blog',
icon: Library,
},
{
name: 'Projects',
href: '/projects',
icon: Cog,
}
];
</script>
{#each menuItems as item}
{@const Icon = item.icon}
<a href={item.href}>
<Icon />
<span>{item.name}</span>
</a>
{/each}
```
:::
For more details about typing the `svelte:component` directive, see the [Svelte documentation](https://svelte.dev/docs/typescript#types-componenttype).
## With Lucide Lab or custom icons
[Lucide Lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
They can be used by using the `Icon` component.
All props like the regular Lucide icons can be passed to adjust the icon appearance.
### Using the `Icon` component
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
```svelte
<script>
import { Icon } from '@lucide/svelte';
import { pear, sausage } from '@lucide/lab';
</script>
<Icon iconNode={pear} />
<Icon iconNode={sausage} color="red"/>
```
## One generic icon component
It is possible to create one generic icon component to load icons, but it is not recommended.
::: danger
The example below imports all ES Modules, so exercise caution when using it. Importing all icons will significantly increase the build size of the application, negatively affecting its performance. This is especially important when using bundlers like `Webpack`, `Rollup`, or `Vite`.
:::
### Icon Component Example
::: code-group
```svelte [Svelte 5]
<script>
import * as icons from '@lucide/svelte';
let { name, ...props } = $props();
const Icon = icons[name];
</script>
<Icon {...props} />
```
```svelte [Svelte 4]
<script>
import * as icons from '@lucide/svelte';
export let name;
</script>
<svelte:component this={icons[name]} {...$$props} />
```
:::
#### Using the Icon Component
```svelte
<script>
import LucideIcon from './LucideIcon';
</script>
<LucideIcon name="Menu" />
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```svelte
<script>
import { Check } from '@lucide/svelte';
</script>
<Check aria-label="Task completed" />
```
For best practices on accessibility, please see our [accessibility guide](../accessibility.md).
-171
View File
@@ -1,171 +0,0 @@
# Lucide Vue
Vue 2 components for Lucide icons that integrate with Vue's Options API and template system. Each icon is a Vue component that renders as an inline SVG, providing familiar Vue development patterns for legacy applications still using Vue 2.
**What you can accomplish:**
- Use icons as Vue 2 components with Options API integration
- Maintain legacy Vue 2 applications with modern icon components
- Integrate with Vue 2's template system and component lifecycle
- Build applications using Vue 2's familiar syntax and patterns
- Bridge the gap while planning migration to Vue 3
## Installation
::: code-group
```sh [pnpm]
pnpm add @lucide/vue
```
```sh [yarn]
yarn add @lucide/vue
```
```sh [npm]
npm install @lucide/vue
```
```sh [bun]
bun add @lucide/vue
```
:::
## How to use
Lucide is built with ES Modules, so it's completely tree-shakable.
Each icon can be imported as a Vue component, which renders an inline SVG Element. This way only the icons that are imported into your project are included in the final bundle. The rest of the icons are tree-shaken away.
### Example
You can pass additional props to adjust the icon.
```vue
<script setup>
import { Camera } from '@lucide/vue';
</script>
<template>
<Camera
color="red"
:size="32"
/>
</template>
```
## Props
| name | type | default |
| ----------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `stroke-width` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
| `default-class` | *string* | lucide-icon |
### Applying props
To customize the appearance of an icon, you can pass custom properties as props directly to the component. The component accepts all SVG attributes as props, which allows flexible styling of the SVG elements. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
```vue
<template>
<Camera fill="red" />
</template>
```
## With Lucide Lab or custom icons
[Lucide Lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
They can be used by using the `Icon` component.
All props like regular Lucide icons can be passed to adjust the icon appearance.
### Using the `Icon` component
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
```vue
<script setup>
import { Icon } from '@lucide/vue';
import { baseball } from '@lucide/lab';
</script>
<template>
<Icon :iconNode="baseball" />
</template>
```
## One generic icon component
It is possible to create one generic icon component to load icons, but it is not recommended.
::: danger
The example below imports all ES Modules, so exercise caution when using it. Importing all icons will significantly increase the build size of the application, negatively affecting its performance. This is especially important when using bundlers like `Webpack`, `Rollup`, or `Vite`.
:::
### Icon Component Example
```vue
<script setup>
import { computed } from 'vue';
import * as icons from "@lucide/vue";
const props = defineProps({
name: {
type: String,
required: true
},
size: Number,
color: String,
strokeWidth: Number,
defaultClass: String
})
const icon = computed(() => icons[props.name]);
</script>
<template>
<component
:is="icon"
:size="size"
:color="color"
:stroke-width="strokeWidth" :default-class="defaultClass"
/>
</template>
```
### Using the Icon Component
All other props listed above also work on the `Icon` Component.
```vue
<template>
<div id="app">
<Icon name="Airplay" />
</div>
</template>
```
## Accessibility
By default, we hide icons from screen readers using `aria-hidden="true"`.
You can add accessibility attributes using aria-labels.
```vue
<script setup>
import { Check } from '@lucide/vue';
</script>
<template>
<Check
color="red"
:size="32"
aria-label="Task completed"
/>
</template>
```
For best practices on accessibility, please see our [accessibility guide](../accessibility.md).
+15 -11
View File
@@ -2,6 +2,7 @@
title: Combining icons - Preact
description: Learn how to combine multiple icons into a single icon nested SVG elements in your Preact application.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/SandpackPreact.vue'
</script>
@@ -14,18 +15,21 @@ This is useful if you want to create custom icons by combining existing ones.
::: sandpack {showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="lucide-preact"}
```jsx App.js [active]
import { Scan, User } from "lucide-preact";
import { h } from "preact";
import { Scan, User } from 'lucide-preact';
import { h } from 'preact';
function App() {
return (
<div className="app">
<Scan size={48}>
<Scan
size={48}
nonScalingStroke
>
<User
size={12}
x={6}
y={6}
absoluteStrokeWidth
nonScalingStroke
/>
</Scan>
</div>
@@ -33,8 +37,8 @@ function App() {
}
export default App;
```
:::
This is valid, since [SVGs can be nested](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/svg#nested_svg_element), and all SVG properties are supported on the icons.
@@ -55,8 +59,8 @@ For example, you can add a notification badge to an icon by using the `circle` S
::: sandpack {showTabs=false editorHeight=580 editorWidthPercentage=60 dependencies="lucide-preact"}
```jsx App.js [active]
import { Mail } from "lucide-preact";
import { h } from "preact";
import { Mail } from 'lucide-preact';
import { h } from 'preact';
function App() {
const hasUnreadMessages = true;
@@ -79,8 +83,8 @@ function App() {
}
export default App;
```
:::
### Example with text element
@@ -90,8 +94,8 @@ You can also use the `text` SVG element to add text to your icon.
::: sandpack {showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="lucide-preact"}
```jsx App.js [active]
import { File } from "lucide-preact";
import { h } from "preact";
import { File } from 'lucide-preact';
import { h } from 'preact';
function App() {
return (
@@ -112,6 +116,6 @@ function App() {
}
export default App;
```
:::
+10 -10
View File
@@ -2,6 +2,7 @@
title: Global Styling - Preact
description: Learn how to style all Lucide icons globally in your Preact application using CSS or the Lucide context provider.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/SandpackPreact.vue'
</script>
@@ -34,8 +35,8 @@ const App = () => (
This will apply the `color`, `size` and `strokeWidth` props to all icons that are children of the `LucideProvider`.
## Style by using CSS
Styling icons is easy to accomplish using CSS.
Every icon has a class attribute applied called `lucide`. This class name can be used in the CSS file to target all icons that are being used within the app.
@@ -74,9 +75,9 @@ import {
Sandwich,
Wine,
Dessert,
} from "lucide-preact";
import { h } from "preact";
import "./icon.css";
} from 'lucide-preact';
import { h } from 'preact';
import './icon.css';
function App() {
return (
@@ -99,10 +100,9 @@ export default App;
:::
### Non-scaling strokes
### Absolute stroke width
For global absolute stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [absolute-stroke-width](../basics/stroke-width.md#absolute-stroke-width) for more info.
For global non-scaling stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [non-scaling strokes](../basics/stroke-width.md#non-scaling-strokes) for more info.
::: sandpack {editorHeight=480 editorWidthPercentage=60 dependencies="lucide-preact"}
@@ -136,9 +136,9 @@ import {
Map,
CloudMoon,
Sparkles,
} from "lucide-preact";
import { h } from "preact";
import "./icon.css";
} from 'lucide-preact';
import { h } from 'preact';
import './icon.css';
function App() {
return (
+12 -1
View File
@@ -2,6 +2,7 @@
title: Typescript - Preact
description: Learn about the different types exported by the `lucide-preact` package and how to use them in your Preact application.
---
# TypeScript
List of exported types from the `lucide-preact` package.
@@ -16,12 +17,17 @@ interface LucideProps {
size?: number | string;
color?: string;
strokeWidth?: number;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
[key: string]: any; // Any other SVG attributes
}
```
### Using `LucideProps`
You can use the `LucideProps` interface to type your custom icon components or when you need to work with icon props.
```tsx
@@ -76,6 +82,7 @@ type IconNode = [elementName: string, attrs: Record<string, string | number>][];
```
### Using `IconNode`
You can use the `IconNode` type when you need to work with the raw SVG structure of an icon.
```tsx
@@ -89,7 +96,11 @@ const customIcon: IconNode = [
const MyCustomIcon = () => {
return (
<Icon iconNode={customIcon} size={24} color="blue" />
<Icon
iconNode={customIcon}
size={24}
color="blue"
/>
);
};
+15 -12
View File
@@ -1,7 +1,8 @@
---
title: Stroke width - Preact
description: Learn how to adjust the stroke width of icons in your Preact application using the `strokeWidth` prop or adjust the strokeWidth appearance using the `absoluteStrokeWidth` prop.
description: Learn how to adjust the stroke width of icons in your Preact application using the `strokeWidth` prop or adjust the strokeWidth appearance using the `nonScalingStroke` prop.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/SandpackPreact.vue'
</script>
@@ -18,8 +19,8 @@ The `strokeWidth` can be adjusted to create a different look of the icons.
::: sandpack {showTabs=false editorHeight=320 editorWidthPercentage=60 dependencies="lucide-preact"}
```jsx App.js [active]
import { h } from "preact";
import { FolderLock } from "lucide-preact";
import { h } from 'preact';
import { FolderLock } from 'lucide-preact';
function App() {
return (
@@ -31,34 +32,35 @@ function App() {
export default App;
```
:::
## Absolute stroke width
## Non-scaling strokes
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `absoluteStrokeWidth` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `nonScalingStroke` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
This means that when `absoluteStrokeWidth` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
This means that when `nonScalingStroke` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
Note `2px` is the default stroke width for a Lucide icon, this can be adjusted to all sizes.
![Absolute stroke width comparison](../../../images/absolute-stroke-width-compare.png?raw=true "Absolute stroke width comparison")
<!--@include: ../../../images/non-scaling-stroke-compare.svg -->
### Adjusting stroke width with `absoluteStrokeWidth` prop
### Adjusting stroke width with `nonScalingStroke` prop
Setting `absoluteStrokeWidth` to `true` will make the stroke width absolute.
Setting `nonScalingStroke` to `true` will make the stroke width non-scaling.
::: sandpack {showTabs=false editorHeight=340 editorWidthPercentage=60 dependencies="lucide-preact"}
```jsx App.js [active]
import { h } from "preact";
import { RollerCoaster } from "lucide-preact";
import { h } from 'preact';
import { RollerCoaster } from 'lucide-preact';
function App() {
return (
<div className="app">
<RollerCoaster
size={96}
absoluteStrokeWidth={true}
nonScalingStroke
/>
</div>
);
@@ -66,4 +68,5 @@ function App() {
export default App;
```
:::
+14 -7
View File
@@ -2,6 +2,7 @@
title: Getting started - Preact
description: This guide will help you get started with Lucide in your Preact project.
---
<script setup lang="ts">
import OverviewLink from '../../.vitepress/theme/components/base/OverviewLink.vue'
import OverviewLinkGrid from '../../.vitepress/theme/components/base/OverviewLinkGrid.vue'
@@ -56,19 +57,25 @@ export default App;
To customize the appearance of an icon, you can use the following props:
| name | type | default |
| --------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `strokeWidth` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
| name | type | default |
| ------------------ | --------- | ------------ |
| `size` | _number_ | 24 |
| `color` | _string_ | currentColor |
| `strokeWidth` | _number_ | 2 |
| `nonScalingStroke` | _boolean_ | false |
Because icons render as SVG elements, all standard SVG attributes can also be applied as props. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
```jsx
// Usage
const App = () => {
return <Camera size={48} color="red" strokeWidth={1} />;
return (
<Camera
size={48}
color="red"
strokeWidth={1}
/>
);
};
```
@@ -16,12 +16,12 @@ import { Scan, User} from "lucide-react-native";
const App = () => {
return (
<View style={styles.container}>
<Scan size={48}>
<Scan size={48} nonScalingStroke>
<User
size={12}
x={6}
y={6}
absoluteStrokeWidth
nonScalingStroke
/>
</Scan>
</View>
+12 -1
View File
@@ -2,6 +2,7 @@
title: Typescript - React Native
description: Learn about the different types exported by the `lucide-react-native` package and how to use them in your React Native application.
---
# TypeScript Support
List of exported types from the `lucide-react-native` package.
@@ -16,12 +17,17 @@ interface LucideProps {
size?: number | string;
color?: string;
strokeWidth?: number;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
[key: string]: any; // Any other SVG props, supported by react-native-svg
}
```
### Using `LucideProps`
You can use the `LucideProps` interface to type your custom icon components or when you need to work with icon props.
```tsx
@@ -75,6 +81,7 @@ type IconNode = [elementName: string, attrs: Record<string, string | number>][];
```
### Using `IconNode`
You can use the `IconNode` type when you need to work with the raw SVG structure of an icon.
```tsx
@@ -88,7 +95,11 @@ const customIcon: IconNode = [
const MyCustomIcon = () => {
return (
<Icon iconNode={customIcon} size={24} color="blue" />
<Icon
iconNode={customIcon}
size={24}
color="blue"
/>
);
};
+10 -9
View File
@@ -1,7 +1,8 @@
---
title: Stroke width - React Native
description: Learn how to adjust the stroke width of icons in your React Native application using the `strokeWidth` prop or adjust the strokeWidth appearance using the `absoluteStrokeWidth` prop.
description: Learn how to adjust the stroke width of icons in your React Native application using the `strokeWidth` prop or adjust the strokeWidth appearance using the `nonScalingStroke` prop.
---
# Stroke width
All icons are designed with SVG elements using strokes.
@@ -29,19 +30,19 @@ const App = () => {
export default App;
```
## Absolute stroke width
## Non-scaling strokes
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `absoluteStrokeWidth` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `nonScalingStroke` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
This means that when `absoluteStrokeWidth` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
This means that when `nonScalingStroke` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
Note `2px` is the default stroke width for a Lucide icon, this can be adjusted to all sizes.
![Absolute stroke width comparison](../../../images/absolute-stroke-width-compare.png?raw=true "Absolute stroke width comparison")
<!--@include: ../../../images/non-scaling-stroke-compare.svg -->
### Adjusting stroke width with `absoluteStrokeWidth` prop
### Adjusting stroke width with `nonScalingStroke` prop
Setting `absoluteStrokeWidth` to `true` will make the stroke width absolute.
Setting `nonScalingStroke` to `true` will make the stroke width non-scaling.
```SnackPlayer name=State&ext=js&dependencies=react-native-svg,lucide-react-native
import React, {useState, useEffect} from 'react';
@@ -55,7 +56,7 @@ const App = () => {
<View style={style}>
<RollerCoaster
size={96}
absoluteStrokeWidth={true}
nonScalingStroke
/>
</View>
);
@@ -67,7 +68,7 @@ export default App;
<!-- <Sandpack
template="react"
:theme="sandpackTheme"
:files="absoluteStrokeWidth"
:files="nonScalingStroke"
:customSetup='{
dependencies: {
"lucide-react": "latest"
+14 -7
View File
@@ -2,6 +2,7 @@
title: Getting started - React Native
description: This guide will help you get started with Lucide in your React Native project.
---
<script setup>
import OverviewLink from '../../.vitepress/theme/components/base/OverviewLink.vue'
import OverviewLinkGrid from '../../.vitepress/theme/components/base/OverviewLinkGrid.vue'
@@ -58,19 +59,25 @@ export default App;
To customize the appearance of an icon, you can use the following props:
| name | type | default |
| --------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `strokeWidth` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
| name | type | default |
| ------------------ | --------- | ------------ |
| `size` | _number_ | 24 |
| `color` | _string_ | currentColor |
| `strokeWidth` | _number_ | 2 |
| `nonScalingStroke` | _boolean_ | false |
Because icons render as SVG elements, all standard SVG attributes can also be applied as props. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
```jsx
// Usage
const App = () => {
return <Camera size={48} color="red" strokeWidth={1} />;
return (
<Camera
size={48}
color="red"
strokeWidth={1}
/>
);
};
```
+2 -2
View File
@@ -19,12 +19,12 @@ import { Scan, User } from "lucide-react";
function App() {
return (
<div className="app">
<Scan size={48}>
<Scan size={48} nonScalingStroke>
<User
size={12}
x={6}
y={6}
absoluteStrokeWidth
nonScalingStroke
/>
</Scan>
</div>
+2 -2
View File
@@ -99,9 +99,9 @@ export default App;
:::
### Absolute stroke width
### Non-scaling strokes
For global absolute stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [absolute-stroke-width](../basics/stroke-width.md#absolute-stroke-width) for more info.
For global non-scaling stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [non-scaling strokes](../basics/stroke-width.md#non-scaling-strokes) for more info.
::: sandpack {template=react editorHeight=300 dependencies="lucide-react"}
+4
View File
@@ -16,6 +16,10 @@ interface LucideProps {
size?: number | string;
color?: string;
strokeWidth?: number;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
[key: string]: any; // Any other SVG attributes
}
+8 -8
View File
@@ -1,6 +1,6 @@
---
title: Stroke width - React
description: Learn how to adjust the stroke width of icons in your React application using the `strokeWidth` prop or adjust the strokeWidth appearance using the `absoluteStrokeWidth` prop.
description: Learn how to adjust the stroke width of icons in your React application using the `strokeWidth` prop or adjust the strokeWidth appearance using the `nonScalingStroke` prop.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/Sandpack.vue'
@@ -33,19 +33,19 @@ export default App;
:::
## Absolute stroke width
## Non-scaling strokes
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `absoluteStrokeWidth` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `nonScalingStroke` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
This means that when `absoluteStrokeWidth` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
This means that when `nonScalingStroke` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
Note `2px` is the default stroke width for a Lucide icon, this can be adjusted to all sizes.
![Absolute stroke width comparison](../../../images/absolute-stroke-width-compare.png?raw=true "Absolute stroke width comparison")
<!--@include: ../../../images/non-scaling-stroke-compare.svg -->
### Adjusting stroke width with `absoluteStrokeWidth` prop
### Adjusting stroke width with `nonScalingStroke` prop
Setting `absoluteStrokeWidth` to `true` will make the stroke width absolute.
Setting `nonScalingStroke` to `true` will make the stroke width non-scaling.
::: sandpack {template=react showTabs=false editorHeight=320 editorWidthPercentage=60 dependencies="lucide-react"}
@@ -57,7 +57,7 @@ function App() {
<div className="app">
<RollerCoaster
size={96}
absoluteStrokeWidth={true}
nonScalingStroke
/>
</div>
);
+6 -6
View File
@@ -54,12 +54,12 @@ export default App;
To customize the appearance of an icon, you can use the following props:
| name | type | default |
| --------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `strokeWidth` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
| name | type | default |
|--------------------| --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `strokeWidth` | *number* | 2 |
| `nonScalingStroke` | *boolean* | false |
Because icons render as SVG elements, all standard SVG attributes can also be applied as props. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
+6 -2
View File
@@ -2,6 +2,7 @@
title: Combining icons - Solid
description: Learn how to combine multiple icons into a single icon nested SVG elements in your Solid application.
---
<!-- Not working yet -->
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/Sandpack.vue'
@@ -21,12 +22,15 @@ import User from 'lucide-solid/icons/user';
function App() {
return (
<div class="app">
<Scan size={48}>
<Scan
size={48}
nonScalingStroke
>
<User
size={12}
x={6}
y={6}
absoluteStrokeWidth
nonScalingStroke
/>
</Scan>
</div>
+7 -6
View File
@@ -2,6 +2,7 @@
title: Global Styling - Solid
description: Learn how to style all Lucide icons globally in your Solid application using CSS or the Lucide context provider.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/Sandpack.vue'
</script>
@@ -36,8 +37,8 @@ const App = () => (
This will apply the `color`, `size` and `strokeWidth` props to all icons that are children of the `LucideProvider`.
## Style by using CSS
Styling icons is easy to accomplish using CSS.
Every icon has a class attribute applied called `lucide`. This class name can be used in the CSS file to target all icons that are being used within the app.
@@ -75,7 +76,7 @@ import IceCream2 from 'lucide-solid/icons/ice-cream-2';
import Sandwich from 'lucide-solid/icons/sandwich';
import Wine from 'lucide-solid/icons/wine';
import Dessert from 'lucide-solid/icons/dessert';
import "./icon.css";
import './icon.css';
function App() {
return (
@@ -95,11 +96,12 @@ function App() {
export default App;
```
:::
### Absolute stroke width
### Non-scaling strokes
For global absolute stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [absolute-stroke-width](../basics/stroke-width.md#absolute-stroke-width) for more info.
For global non-scaling stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [non-scaling strokes](../basics/stroke-width.md#non-scaling-strokes) for more info.
::: sandpack {template=vite-solid editorHeight=300 editorWidthPercentage=60 dependencies="lucide-solid"}
@@ -120,7 +122,6 @@ For global absolute stroke width styling the `vector-effect: non-scaling-stroke`
grid-template-rows: 1fr 1fr 1fr;
gap: 6px;
}
```
```tsx App.tsx
@@ -133,7 +134,7 @@ import Axe from 'lucide-solid/icons/axe';
import Map from 'lucide-solid/icons/map';
import CloudMoon from 'lucide-solid/icons/cloud-moon';
import Sparkles from 'lucide-solid/icons/sparkles';
import "./icon.css";
import './icon.css';
function App() {
return (
+12 -1
View File
@@ -2,6 +2,7 @@
title: Typescript - Solid
description: Learn about the different types exported by the `lucide-solid` package and how to use them in your Solid application.
---
# TypeScript Support
List of exported types from the `lucide-solid` package.
@@ -16,12 +17,17 @@ interface LucideProps extends SVGAttributes {
size?: number | string;
color?: string;
strokeWidth?: number;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
[key: string]: any; // Any other SVG attributes
}
```
### Using `LucideProps`
You can use the `LucideProps` interface to type your custom icon components or when you need to work with icon props.
```tsx
@@ -76,6 +82,7 @@ type IconNode = [elementName: string, attrs: Record<string, string | number>][];
```
### Using `IconNode`
You can use the `IconNode` type when you need to work with the raw SVG structure of an icon.
```tsx
@@ -89,7 +96,11 @@ const customIcon: IconNode = [
const MyCustomIcon = () => {
return (
<Icon iconNode={customIcon} size={24} color="blue" />
<Icon
iconNode={customIcon}
size={24}
color="blue"
/>
);
};
+11 -8
View File
@@ -1,7 +1,8 @@
---
title: Stroke width - Solid
description: Learn how to adjust the stroke width of icons in your Solid application using the `strokeWidth` prop or adjust the strokeWidth appearance using the `absoluteStrokeWidth` prop.
description: Learn how to adjust the stroke width of icons in your Solid application using the `strokeWidth` prop or adjust the strokeWidth appearance using the `nonScalingStroke` prop.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/Sandpack.vue'
</script>
@@ -30,21 +31,22 @@ function App() {
export default App;
```
:::
## Absolute stroke width
## Non-scaling strokes
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `absoluteStrokeWidth` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `nonScalingStroke` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
This means that when `absoluteStrokeWidth` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
This means that when `nonScalingStroke` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
Note `2px` is the default stroke width for a Lucide icon, this can be adjusted to all sizes.
![Absolute stroke width comparison](../../../images/absolute-stroke-width-compare.png?raw=true "Absolute stroke width comparison")
<!--@include: ../../../images/non-scaling-stroke-compare.svg -->
### Adjusting stroke width with `absoluteStrokeWidth` prop
### Adjusting stroke width with `nonScalingStroke` prop
Setting `absoluteStrokeWidth` to `true` will make the stroke width absolute.
Setting `nonScalingStroke` to `true` will make the stroke width non-scaling.
::: sandpack {template=vite-solid showTabs=false editorHeight=320 editorWidthPercentage=60 dependencies="lucide-solid"}
@@ -56,7 +58,7 @@ function App() {
<div class="app">
<RollerCoaster
size={96}
absoluteStrokeWidth={true}
nonScalingStroke
/>
</div>
);
@@ -64,4 +66,5 @@ function App() {
export default App;
```
:::
+13 -7
View File
@@ -57,19 +57,25 @@ export default App;
To customize the appearance of an icon, you can use the following props:
| name | type | default |
| --------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `strokeWidth` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
| name | type | default |
| ------------------ | --------- | ------------ |
| `size` | _number_ | 24 |
| `color` | _string_ | currentColor |
| `strokeWidth` | _number_ | 2 |
| `nonScalingStroke` | _boolean_ | false |
Because icons render as SVG elements, all standard SVG attributes can also be applied as props. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
```jsx
// Usage
const App = () => {
return <Camera size={48} color="red" strokeWidth={1} />;
return (
<Camera
size={48}
color="red"
strokeWidth={1}
/>
);
};
```
@@ -2,6 +2,7 @@
title: Combining icons - Svelte
description: Learn how to combine multiple icons into a single icon nested SVG elements in your Svelte application.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/SandpackSvelte.vue';
</script>
@@ -20,12 +21,12 @@ import User from '@lucide/svelte/icons/user';
</script>
<div class="app">
<Scan size="48">
<Scan size="48" nonScalingStroke>
<User
size="12"
x="6"
y="6"
absoluteStrokeWidth
nonScalingStroke
/>
</Scan>
</div>
+5 -2
View File
@@ -2,6 +2,7 @@
title: Global Styling - Svelte
description: Learn how to style all Lucide icons globally in your Svelte application using CSS or the Lucide context provider.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/SandpackSvelte.vue';
</script>
@@ -89,11 +90,12 @@ import "./icon.css";
<Dessert />
</div>
```
:::
### Absolute stroke width
### Non-scaling strokes
For global absolute stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [absolute-stroke-width](../basics/stroke-width.md#absolute-stroke-width) for more info.
For global non-scaling stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [non-scaling strokes](../basics/stroke-width.md#non-scaling-strokes) for more info.
::: sandpack {template=vite-svelte editorHeight=420 editorWidthPercentage=55}
@@ -143,4 +145,5 @@ import "./icon.css";
<Sparkles />
</div>
```
:::
+15 -3
View File
@@ -2,6 +2,7 @@
title: Typescript - Svelte
description: Learn about the different types exported by the `@lucide/svelte` package and how to use them in your Svelte application.
---
# TypeScript Support
List of exported types from the `@lucide/svelte` package.
@@ -17,6 +18,10 @@ interface LucideProps extends SVGAttributes<SVGSVGElement> {
color?: string;
size?: number | string;
strokeWidth?: number | string;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
children?: Snippet;
[key: string]: any; // Any other SVG attributes
@@ -24,9 +29,11 @@ interface LucideProps extends SVGAttributes<SVGSVGElement> {
```
### Using `LucideProps`
You can use the `LucideProps` interface to type props for your custom icon components.
::: code-group
```svelte [IconWrapper.svelte]
<script lang="ts">
import { Camera, type LucideProps } from '@lucide/svelte';
@@ -40,6 +47,7 @@ let props: LucideProps = $props();
</div>
</template>
```
:::
## `LucideIcon`
@@ -49,7 +57,7 @@ Type for individual icon components, this is use full when you want to type a va
```ts
import type { Component } from 'svelte';
type LucideIcon = Component<LucideProps>
type LucideIcon = Component<LucideProps>;
```
### Using `LucideIcon`
@@ -135,6 +143,7 @@ You can use the `LucideIcon` type when you need to work with icon components dir
{/each}
```
:::
## `IconNode`
@@ -144,15 +153,17 @@ Not commonly used directly in application code. But can be useful for advanced u
```ts
type IconNode = [
elementName: 'circle' | 'ellipse'| 'g' | 'line' | 'path' | 'polygon' | 'polyline' | 'rect',
attrs: SVGAttributes<SVGSVGElement>
elementName: 'circle' | 'ellipse' | 'g' | 'line' | 'path' | 'polygon' | 'polyline' | 'rect',
attrs: SVGAttributes<SVGSVGElement>,
][];
```
### Using `IconNode`
You can use the `IconNode` type when you need to work with the raw SVG structure of an icon.
::: code-group
```svelte [CustomIcon.svelte]
<script lang="ts">
import { type IconNode, Icon } from '@lucide/svelte';
@@ -166,4 +177,5 @@ const customIcon: IconNode = [
<Icon iconNode={customIcon} size="24" color="blue" />
```
:::
+10 -8
View File
@@ -1,7 +1,8 @@
---
title: Stroke width - Svelte
description: Learn how to adjust the stroke width of icons in your Svelte application using the `strokeWidth` prop or adjust the strokeWidth appearance using the `absoluteStrokeWidth` prop.
description: Learn how to adjust the stroke width of icons in your Svelte application using the `strokeWidth` prop or adjust the strokeWidth appearance using the `nonScalingStroke` prop.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/SandpackSvelte.vue'
</script>
@@ -24,21 +25,22 @@ import FolderLock from "@lucide/svelte/icons/folder-lock";
<FolderLock strokeWidth={1} />
```
:::
## Absolute stroke width
## Non-scaling strokes
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `absoluteStrokeWidth` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `nonScalingStroke` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
This means that when `absoluteStrokeWidth` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
This means that when `nonScalingStroke` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
Note `2px` is the default stroke width for a Lucide icon, this can be adjusted to all sizes.
![Absolute stroke width comparison](../../../images/absolute-stroke-width-compare.png?raw=true "Absolute stroke width comparison")
<!--@include: ../../../images/non-scaling-stroke-compare.svg -->
### Adjusting stroke width with `absoluteStrokeWidth` prop
### Adjusting stroke width with `nonScalingStroke` prop
Setting `absoluteStrokeWidth` to `true` will make the stroke width absolute.
Setting `nonScalingStroke` to `true` will make the stroke width non-scaling.
::: sandpack {template=vite-svelte editorHeight=240 editorWidthPercentage=60}
@@ -49,7 +51,7 @@ import RollerCoaster from "@lucide/svelte/icons/roller-coaster";
<RollerCoaster
size={96}
absoluteStrokeWidth
nonScalingStroke
/>
```
+8 -7
View File
@@ -2,6 +2,7 @@
title: Getting started - Svelte
description: This guide will help you get started with Lucide in your Svelte project.
---
<script setup>
import OverviewLink from '../../.vitepress/theme/components/base/OverviewLink.vue'
import OverviewLinkGrid from '../../.vitepress/theme/components/base/OverviewLinkGrid.vue'
@@ -53,13 +54,13 @@ Each icon can be imported as a Svelte component, which renders an inline SVG ele
To customize the appearance of an icon, you can use the following props:
| name | type | default |
| ----------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `stroke-width` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
| `default-class` | *string* | lucide-icon |
| name | type | default |
| ------------------ | --------- | ------------ |
| `size` | _number_ | 24 |
| `color` | _string_ | currentColor |
| `stroke-width` | _number_ | 2 |
| `nonScalingStroke` | _boolean_ | false |
| `default-class` | _string_ | lucide-icon |
Because icons render as SVG elements, all standard SVG attributes can also be applied as props. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
+9 -8
View File
@@ -2,6 +2,7 @@
title: Combining icons - Vue
description: Learn how to combine multiple icons into a single icon nested SVG elements in your Vue application.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/SandpackVue.vue'
</script>
@@ -15,22 +16,24 @@ This is useful if you want to create custom icons by combining existing ones.
```vue src/App.vue [active]
<script setup>
import { Scan, User } from "@lucide/vue";
import { Scan, User } from '@lucide/vue';
</script>
<template>
<div class="app">
<Scan :size="48">
<Scan
:size="48"
nonScalingStroke
>
<User
:size="12"
x="6"
y="6"
absoluteStrokeWidth
nonScalingStroke
/>
</Scan>
</div>
</template>
```
:::
@@ -54,7 +57,7 @@ For example, you can add a notification badge to an icon by using the `circle` S
```vue src/App.vue [active]
<script setup>
import { Mail } from "@lucide/vue";
import { Mail } from '@lucide/vue';
const hasUnreadMessages = true;
</script>
@@ -73,7 +76,6 @@ const hasUnreadMessages = true;
</Mail>
</div>
</template>
```
:::
@@ -86,7 +88,7 @@ You can also use the `text` SVG element to add text to your icon.
```vue src/App.vue [active]
<script setup>
import { File } from "@lucide/vue";
import { File } from '@lucide/vue';
</script>
<template>
@@ -104,7 +106,6 @@ import { File } from "@lucide/vue";
</File>
</div>
</template>
```
:::
+9 -7
View File
@@ -2,6 +2,7 @@
title: Global Styling - Vue
description: Learn how to style all Lucide icons globally in your Vue application using CSS or the Lucide context provider.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/SandpackVue.vue'
</script>
@@ -74,9 +75,9 @@ import {
Sandwich,
Wine,
Dessert,
} from "@lucide/vue";
} from '@lucide/vue';
import "./icon.css";
import './icon.css';
</script>
<template>
@@ -93,11 +94,12 @@ import "./icon.css";
</div>
</template>
```
:::
### Absolute stroke width
### Non-scaling strokes
For global absolute stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [absolute-stroke-width](../basics/stroke-width.md#absolute-stroke-width) for more info.
For global non-scaling stroke width styling the `vector-effect: non-scaling-stroke` CSS property can be applied to the children. This will keep the stroke-width the same size no matter the size of the icon. See [non-scaling strokes](../basics/stroke-width.md#non-scaling-strokes) for more info.
::: sandpack {template=vue editorHeight=300 editorWidthPercentage=55 dependencies="@lucide/vue"}
@@ -132,9 +134,9 @@ import {
Map,
CloudMoon,
Sparkles,
} from "@lucide/vue";
} from '@lucide/vue';
import "./icon.css";
import './icon.css';
</script>
<template>
@@ -150,6 +152,6 @@ import "./icon.css";
<Sparkles />
</div>
</template>
```
:::
+22 -2
View File
@@ -2,6 +2,7 @@
title: Typescript - Vue
description: Learn about the different types exported by the `@lucide/vue` package and how to use them in your Vue application.
---
# TypeScript Support
List of exported types from the `@lucide/vue` package.
@@ -16,15 +17,21 @@ interface LucideProps {
size?: number | string;
color?: string;
strokeWidth?: number;
nonScalingStroke?: boolean;
/**
* @deprecated
*/
absoluteStrokeWidth?: boolean;
[key: string]: any; // Any other SVG attributes
}
```
### Using `LucideProps`
You can use the `LucideProps` interface to type props for your custom icon components.
::: code-group
```vue [IconWrapper.vue]
<script lang="ts" setup>
import { type LucideProps } from '@lucide/vue';
@@ -39,6 +46,7 @@ defineProps<LucideProps>();
</div>
</template>
```
:::
## `LucideIcon`
@@ -54,6 +62,7 @@ type LucideIcon = React.FC<LucideProps>;
You can use the `LucideIcon` type when you need to work with icon components directly.
::: code-group
```vue [IconButton.vue]
<script lang="ts" setup>
import { type LucideProps } from '@lucide/vue';
@@ -67,10 +76,14 @@ defineProps<{
<template>
<button :aria-label="label">
<component :is="icon" :size="16" />
<component
:is="icon"
:size="16"
/>
</button>
</template>
```
:::
## `IconNode`
@@ -83,9 +96,11 @@ type IconNode = [elementName: string, attrs: Record<string, string | number>][];
```
### Using `IconNode`
You can use the `IconNode` type when you need to work with the raw SVG structure of an icon.
::: code-group
```vue [CustomIcon.vue]
<script lang="ts" setup>
import { type IconNode, Icon } from '@lucide/vue';
@@ -98,7 +113,12 @@ const customIcon: IconNode = [
</script>
<template>
<Icon :iconNode="customIcon" size="24" color="blue" />
<Icon
:iconNode="customIcon"
size="24"
color="blue"
/>
</template>
```
:::
+13 -11
View File
@@ -1,7 +1,8 @@
---
title: Stroke width - Vue
description: Learn how to adjust the stroke width of icons in your Vue application using the `strokeWidth` prop or adjust the strokeWidth appearance using the `absoluteStrokeWidth` prop.
description: Learn how to adjust the stroke width of icons in your Vue application using the `strokeWidth` prop or adjust the strokeWidth appearance using the `nonScalingStroke` prop.
---
<script setup>
import Sandpack from '~/.vitepress/theme/components/editors/SandpackVue.vue'
</script>
@@ -19,42 +20,43 @@ The `strokeWidth` can be adjusted to create a different look of the icons.
```vue src/App.vue [active]
<script setup>
import { FolderLock } from "@lucide/vue";
import { FolderLock } from '@lucide/vue';
</script>
<template>
<FolderLock :strokeWidth="1" />
</template>
```
:::
## Absolute stroke width
## Non-scaling strokes
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `absoluteStrokeWidth` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
When adjusting the `size` prop the size of the stroke width will be relative to the size of the icon, this is the default SVG behavior. The `nonScalingStroke` prop is introduced to adjust this behavior to make the stroke width constant no matter the size of the icon.
This means that when `absoluteStrokeWidth` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
This means that when `nonScalingStroke` is enabled and the `size` of the icons is set to `48px` the `strokeWidth` will still be `2px` on the screen.
Note `2px` is the default stroke width for a Lucide icon, this can be adjusted to all sizes.
![Absolute stroke width comparison](../../../images/absolute-stroke-width-compare.png?raw=true "Absolute stroke width comparison")
<!--@include: ../../../images/non-scaling-stroke-compare.svg -->
### Adjusting stroke width with `absoluteStrokeWidth` prop
### Adjusting stroke width with `nonScalingStroke` prop
Setting `absoluteStrokeWidth` to `true` will make the stroke width absolute.
Setting `nonScalingStroke` to `true` will make the stroke width non-scaling.
::: sandpack {template=vue showTabs=false editorHeight=320 editorWidthPercentage=60 dependencies="@lucide/vue"}
```vue src/App.vue [active]
<script setup>
import { RollerCoaster } from "@lucide/vue";
import { RollerCoaster } from '@lucide/vue';
</script>
<template>
<RollerCoaster
:size="96"
absoluteStrokeWidth
nonScalingStroke
/>
</template>
```
:::
+13 -8
View File
@@ -2,6 +2,7 @@
title: Getting started - Vue
description: This guide will help you get started with Lucide in your Vue project.
---
<script setup>
import OverviewLink from '../../.vitepress/theme/components/base/OverviewLink.vue'
import OverviewLinkGrid from '../../.vitepress/theme/components/base/OverviewLinkGrid.vue'
@@ -55,13 +56,13 @@ import { Camera } from '@lucide/vue';
To customize the appearance of an icon, you can use the following props:
| name | type | default |
| ----------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `stroke-width` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
| `default-class` | *string* | lucide-icon |
| name | type | default |
| ------------------ | --------- | ------------ |
| `size` | _number_ | 24 |
| `color` | _string_ | currentColor |
| `stroke-width` | _number_ | 2 |
| `nonScalingStroke` | _boolean_ | false |
| `default-class` | _string_ | lucide-icon |
### Applying props
@@ -69,7 +70,11 @@ Because icons render as SVG elements, all standard SVG attributes can also be ap
```vue
<template>
<Camera :size="48" color="red" :stroke-width="1" />
<Camera
:size="48"
color="red"
:stroke-width="1"
/>
</template>
```
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 117 KiB

+2 -2
View File
@@ -6,8 +6,8 @@ outline: 2
outlineTitle: Packages
---
<script setup>
import PageContainer from '.vitepress/theme/components/PageContainer.vue'
import PackageList from '.vitepress/theme/components/packages/PackageList.vue'
import PageContainer from '~/.vitepress/theme/components/PageContainer.vue'
import PackageList from '~/.vitepress/theme/components/packages/PackageList.vue'
</script>
<div class="VPDoc content">
+2 -2
View File
@@ -5,8 +5,8 @@ outline: 2
outlineTitle: Showcase
---
<script setup>
import PageContainer from '.vitepress/theme/components/PageContainer.vue'
import ShowcaseList from '.vitepress/theme/components/showcase/ShowcaseList.vue'
import PageContainer from '~/.vitepress/theme/components/PageContainer.vue'
import ShowcaseList from '~/.vitepress/theme/components/showcase/ShowcaseList.vue'
</script>
<div class="VPDoc content">
+5 -40
View File
@@ -13,7 +13,7 @@
},
{
"source": "/docs/:slug(lucide-?.*)",
"destination": "/guide/packages/:slug",
"destination": "/guide/:slug",
"permanent": true
},
{
@@ -58,48 +58,13 @@
"permanent": true
},
{
"source": "/guide/packages/lucide-react",
"destination": "/guide/react",
"source": "/guide/packages/lucide-:package",
"destination": "/guide/:package",
"permanent": true
},
{
"source": "/guide/packages/lucide-vue-next",
"destination": "/guide/vue",
"permanent": true
},
{
"source": "/guide/packages/lucide-vue",
"destination": "/guide/vue",
"permanent": true
},
{
"source": "/guide/packages/lucide-svelte",
"destination": "/guide/svelte",
"permanent": true
},
{
"source": "/guide/packages/lucide-solid",
"destination": "/guide/solid",
"permanent": true
},
{
"source": "/guide/packages/lucide-angular",
"destination": "/guide/angular",
"permanent": true
},
{
"source": "/guide/packages/lucide-preact",
"destination": "/guide/preact",
"permanent": true
},
{
"source": "/guide/packages/lucide-astro",
"destination": "/guide/astro",
"permanent": true
},
{
"source": "/guide/packages/lucide-static",
"destination": "/guide/static",
"source": "/guide/packages/:package",
"destination": "/guide/:package",
"permanent": true
},
{
+1
View File
@@ -60,6 +60,7 @@
"@vitest/coverage-v8": "4.1.10",
"@vitest/ui": "4.1.10",
"ajv-cli": "^5.0.0",
"cpy-cli": "^7.0.0",
"cspell": "^10.0.1",
"dotenv": "^17.4.2",
"eslint": "^10.8.0",
+4 -3
View File
@@ -17,14 +17,15 @@
"scripts": {
"ng": "ng",
"watch": "ng build --watch --configuration development",
"prebuild": "pnpm clean && pnpm copy:license && pnpm build:icons",
"prebuild": "pnpm clean && pnpm copy:license && pnpm copy:utils && pnpm build:icons",
"build": "pnpm prebuild && pnpm build:ng",
"copy:license": "cp ../../LICENSE ./LICENSE",
"copy:utils": "cpy ../../packages/shared/src/build/{buildLucideIconNode.ts,defaultAttributes.ts,types.ts} ../../packages/shared/src/utils/mergeClasses.ts ./src/utils/",
"clean": "rm -rf dist && rm -rf ./src/icons/*.ts",
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --iconFileExtension=.ts --exportFileName=lucide-angular.ts --useDefaultExports=0",
"build:ng": "ng build --configuration production",
"test": "pnpm build:icons && ng test --no-watch",
"test:watch": "ng test",
"test": "pnpm copy:utils && pnpm build:icons && ng test --no-watch",
"test:watch": "pnpm copy:utils && ng test",
"lint": "eslint 'src/**/*.{js,jsx,ts,tsx,html,css,scss}' --quiet --fix",
"e2e": "ng e2e"
},
+7 -1
View File
@@ -19,6 +19,11 @@ export interface LucideConfig {
* @default 2
*/
strokeWidth: number;
/**
* Whether to use absolute stroke width.
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth: boolean;
/**
* Whether stroke width should be scaled to appear uniform regardless of icon size.
* @default false
@@ -31,7 +36,7 @@ export interface LucideConfig {
* }
* ```
*/
absoluteStrokeWidth: boolean;
nonScalingStroke: boolean;
}
/**
@@ -42,6 +47,7 @@ export const lucideDefaultConfig: LucideConfig = {
size: 24,
strokeWidth: 2,
absoluteStrokeWidth: false,
nonScalingStroke: false,
};
/**
@@ -8,7 +8,7 @@ import { LucideBadgeAlert } from './icons/badge-alert';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'lucide-static-hydration-root',
template: `<svg lucideBadgeAlert [absoluteStrokeWidth]="true"></svg>`,
template: `<svg lucideBadgeAlert [nonScalingStroke]="true"></svg>`,
imports: [LucideBadgeAlert],
})
class StaticHydrationRootComponent {}
+43 -9
View File
@@ -47,6 +47,7 @@ describe('LucideIconBase', () => {
let size: WritableSignal<string | number | undefined>;
let strokeWidth: WritableSignal<string | number | undefined>;
let absoluteStrokeWidth: WritableSignal<boolean | undefined>;
let nonScalingStroke: WritableSignal<boolean | undefined>;
const getSvgAttribute = (attr: string) => fixture.nativeElement.getAttribute(attr);
const expectSvgClasses = (classes: string[], svg: SVGElement = fixture.nativeElement) => {
for (const cssClass of classes) {
@@ -63,6 +64,7 @@ describe('LucideIconBase', () => {
inputBinding('size', size),
inputBinding('strokeWidth', strokeWidth),
inputBinding('absoluteStrokeWidth', absoluteStrokeWidth),
inputBinding('nonScalingStroke', nonScalingStroke),
],
});
}
@@ -72,6 +74,7 @@ describe('LucideIconBase', () => {
size = signal(undefined);
strokeWidth = signal(undefined);
absoluteStrokeWidth = signal(undefined);
nonScalingStroke = signal(undefined);
fixture = createComponent();
component = fixture.componentInstance;
});
@@ -135,6 +138,11 @@ describe('LucideIconBase', () => {
expect(getSvgAttribute('width')).toBe('18');
expect(getSvgAttribute('height')).toBe('18');
});
it('should set viewBox', () => {
size.set('18');
fixture.detectChanges();
expect(getSvgAttribute('viewBox')).toBe('0 0 24 24');
});
});
describe('strokeWidth', () => {
@@ -155,22 +163,38 @@ describe('LucideIconBase', () => {
});
describe('absoluteStrokeWidth', () => {
it('should adjust stroke width', () => {
strokeWidth.set(2);
size.set(32);
absoluteStrokeWidth.set(true);
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('1.5');
});
it('should not set vector-effect on children', () => {
absoluteStrokeWidth.set(true);
for (const child of fixture.nativeElement.children) {
expect(child.getAttribute('vector-effect')).toBeNull();
}
});
});
describe('nonScalingStroke', () => {
it('should not adjust stroke width', () => {
strokeWidth.set(2);
size.set(12);
absoluteStrokeWidth.set(true);
nonScalingStroke.set(false);
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('2');
});
it('should not set vector-effect on children', () => {
absoluteStrokeWidth.set(false);
nonScalingStroke.set(false);
fixture.detectChanges();
for (const child of getRenderedChildren()) {
expect(child.getAttribute('vector-effect')).toBeNull();
}
});
it('should set vector-effect on children', () => {
absoluteStrokeWidth.set(true);
nonScalingStroke.set(true);
fixture.detectChanges();
for (const child of getRenderedChildren()) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
@@ -207,6 +231,7 @@ describe('LucideIconBase', () => {
color: 'red',
strokeWidth: 1,
size: 12,
nonScalingStroke: true,
absoluteStrokeWidth: true,
}),
],
@@ -229,12 +254,12 @@ describe('LucideIconBase', () => {
describe('strokeWidth', () => {
it('should use stroke width from config', () => {
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('1');
expect(getSvgAttribute('stroke-width')).toBe('2');
});
it('should use override stroke width from config', () => {
strokeWidth.set(3);
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('3');
expect(getSvgAttribute('stroke-width')).toBe('6');
});
});
describe('size', () => {
@@ -253,14 +278,23 @@ describe('LucideIconBase', () => {
describe('absoluteStrokeWidth', () => {
it('should use absoluteStrokeWidth from config', () => {
fixture.detectChanges();
for (const child of getRenderedChildren()) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
expect(getSvgAttribute('stroke-width')).toBe('2');
});
it('should override absoluteStrokeWidth', () => {
absoluteStrokeWidth.set(false);
fixture.detectChanges();
for (const child of getRenderedChildren()) {
expect(getSvgAttribute('stroke-width')).toBe('1');
});
});
describe('nonScalingStroke', () => {
it('should use nonScalingStroke from config', () => {
for (const child of fixture.nativeElement.children) {
expect(child.getAttribute('vector-effect')).toBe('non-scaling-stroke');
}
});
it('should override nonScalingStroke', () => {
nonScalingStroke.set(false);
for (const child of fixture.nativeElement.children) {
expect(child.getAttribute('vector-effect')).toBeNull();
}
});
+110 -30
View File
@@ -1,8 +1,9 @@
import { Component, computed, inject, input, Signal } from '@angular/core';
import { LUCIDE_CONFIG } from './lucide-config';
import { LucideIconData, Nullable } from './types';
import { LucideIconData, LucideIconNode, Nullable } from './types';
import defaultAttributes from './default-attributes';
import { lucideIconTemplate } from './lucide-icon-template';
import buildLucideIconNode from './utils/buildLucideIconNode';
/**
* @internal
@@ -12,38 +13,17 @@ import { lucideIconTemplate } from './lucide-icon-template';
template: lucideIconTemplate,
host: {
...defaultAttributes,
class: 'lucide',
'[class]': 'iconClasses()',
'[attr.width]': 'size()',
'[attr.height]': 'size()',
'[attr.stroke]': 'color()',
'[attr.stroke-width]': 'strokeWidth()',
'[attr.aria-hidden]': '!title()',
'[attr.class]': 'computedClass()',
'[attr.width]': 'computedWidth()',
'[attr.height]': 'computedHeight()',
'[attr.viewBox]': 'computedViewbox()',
'[attr.stroke]': 'computedStroke()',
'[attr.stroke-width]': 'computedStrokeWidth()',
'[attr.aria-hidden]': 'computedAriaHidden()',
},
})
export abstract class LucideIconBase {
protected abstract readonly icon: Signal<Nullable<LucideIconData>>;
protected readonly iconNodes = computed<LucideIconData['node']>(() => {
const node = this.icon()?.node ?? [];
if (!this.absoluteStrokeWidth()) {
return node;
}
return node.map<LucideIconData['node'][number]>(([name, attrs]) => [
name,
{
'vector-effect': 'non-scaling-stroke',
...attrs,
},
]);
});
protected readonly iconClasses = computed(() => {
const icon = this.icon();
if (!icon) {
return '';
}
const { name, aliases = [] } = icon;
return [name, ...aliases].map((item) => `lucide-${item}`).join(' ');
});
protected readonly iconConfig = inject(LUCIDE_CONFIG);
/**
* An optional accessible label for the icon.
@@ -58,6 +38,10 @@ export abstract class LucideIconBase {
* - If your icon is functional (e.g. used in place of a label), feel free to use this property.
*/
readonly title = input<Nullable<string>>();
/**
* Optional CSS classes for the icon.
*/
readonly class = input<Nullable<string>>();
/**
* Width and height.
* @default 24
@@ -65,6 +49,16 @@ export abstract class LucideIconBase {
readonly size = input(this.iconConfig.size, {
transform: (value: Nullable<string | number>) => value ?? this.iconConfig.size,
});
/**
* Icon width.
* @default 24
*/
readonly width = input(null);
/**
* Icon height.
* @default 24
*/
readonly height = input(null);
/**
* Stroke color.
* @default currentColor
@@ -80,9 +74,95 @@ export abstract class LucideIconBase {
transform: (value: Nullable<string | number>) => value ?? this.iconConfig.strokeWidth,
});
/**
* If set to true, it adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements.
* Whether to use absolute stroke width.
*/
readonly absoluteStrokeWidth = input(this.iconConfig.absoluteStrokeWidth, {
transform: (value: Nullable<boolean>) => value ?? this.iconConfig.absoluteStrokeWidth,
});
/**
* If set to true, it adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements.
*/
readonly nonScalingStroke = input(this.iconConfig.nonScalingStroke, {
transform: (value: Nullable<boolean>) => value ?? this.iconConfig.nonScalingStroke,
});
/**
* @internal
*/
protected readonly computedIcon = computed(() => {
const icon = this.icon();
return icon
? buildLucideIconNode(icon, {
width: this.width() ?? this.size(),
height: this.height() ?? this.size(),
color: this.color(),
strokeWidth: this.strokeWidth(),
absoluteStrokeWidth: this.absoluteStrokeWidth(),
nonScalingStroke: this.nonScalingStroke(),
className: this.class() ?? undefined,
hasA11yProp: this.title() != null,
})
: null;
});
/**
* @internal
*/
protected readonly computedAttributes = computed<LucideIconNode[1] | undefined>(
() => this.computedIcon()?.[1],
);
/**
* @internal
*/
protected readonly computedIconNode = computed(() => this.computedIcon()?.[2] ?? []);
/**
* @internal
*/
protected readonly computedWidth = computed(
() => this.computedAttributes()?.['width'] ?? this.iconConfig.size,
);
/**
* @internal
*/
protected readonly computedHeight = computed(
() => this.computedAttributes()?.['height'] ?? this.iconConfig.size,
);
/**
* @internal
*/
protected readonly computedStroke = computed(
() => this.computedAttributes()?.['stroke'] ?? this.iconConfig.color,
);
/**
* @internal
*/
protected readonly computedStrokeWidth = computed(
() => this.computedAttributes()?.['stroke-width'] ?? this.iconConfig.strokeWidth,
);
/**
* @internal
*/
protected readonly computedAriaHidden = computed(
() => this.computedAttributes()?.['aria-hidden'] === 'true',
);
/**
* @internal
*/
protected readonly computedViewbox = computed(
() => this.computedAttributes()?.['viewBox'] ?? '0 0 24 24',
);
/**
* @internal
*/
protected readonly computedClass = computed(
() => this.computedAttributes()?.['class'] ?? 'lucide',
);
}
+1 -1
View File
@@ -6,7 +6,7 @@ export const lucideIconTemplate = `@if (title(); as titleValue) {
<title>{{ titleValue }}</title>
}
<ng-content select="title"></ng-content>
@for (child of iconNodes(); track child[1]['key'] ?? $index) {
@for (child of computedIconNode(); track child[1]['key'] ?? $index) {
@let attrs = child[1];
@switch (child[0]) {
@case ('path') {
+10 -8
View File
@@ -1,18 +1,16 @@
import { Signal, Type } from '@angular/core';
import {
LucideIconNode as SharedLucideIconNode,
LucideIconData as SharedLucideIconData,
} from './utils/types';
type HtmlAttributes = { [key: string]: string | number };
export type LucideIconNode = readonly [string, HtmlAttributes];
export type LucideIconNode = SharedLucideIconNode;
export type LucideIcons = { [key: string]: LucideIconData };
/**
* A Lucide icon object that fully describes an icon to be displayed.
*/
export type LucideIconData = {
name: string;
size?: number;
node: LucideIconNode[];
aliases?: string[];
};
export type LucideIconData = SharedLucideIconData & { name: string };
/**
* Input signal map of Lucide icon components.
@@ -22,7 +20,11 @@ interface LucideIconProps {
size: Signal<Nullable<number | string>>;
color: Signal<Nullable<string>>;
strokeWidth: Signal<Nullable<number | string>>;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth: Signal<Nullable<boolean>>;
nonScalingStroke: Signal<Nullable<boolean>>;
}
/**
+1 -1
View File
@@ -43,7 +43,7 @@
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts --pretty=false",
"clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.ts",
"copy:license": "cp ../../LICENSE ./LICENSE",
"copy:utils": "mkdir -p ./src/utils && for f in hasA11yProp toKebabCase mergeClasses; do cp -f ../../packages/shared/src/utils/$f.ts ./src/utils/; done",
"copy:utils": "cpy ../../packages/shared/src/build/{buildLucideIconNode.ts,defaultAttributes.ts,types.ts} ../../packages/shared/src/utils/{hasA11yProp.ts,toKebabCase.ts,mergeClasses.ts} ./src/utils/",
"test": "pnpm copy:utils && pnpm build:icons && vitest run",
"test:watch": "pnpm build:icons && vitest run --watch"
},
+21 -21
View File
@@ -1,28 +1,27 @@
import base64SVG from '@lucide/build-icons/utils/base64SVG';
import defineExportTemplate from '@lucide/build-icons/utils/defineExportTemplate';
export default defineExportTemplate(async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
export default defineExportTemplate(
async ({ componentName, iconName, iconData, getSvg, deprecated, deprecationReason }) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
// Astro doesn't need keyed children in loops
const keylessChildren = children.map((c) => {
const [element, { key, ...otherAttrs }] = c;
return [element, otherAttrs];
});
// Astro doesn't need keyed children in loops
const keylessIconData = {
...iconData,
node: iconData.node.map((child) => {
const [element, { key, ...otherAttrs }] = child;
return [element, otherAttrs];
}),
};
// TODO: build-icons' `pretty` is set to false as the prettier
// formatter uses babel which I'm not sure it supports typescript
return `
// TODO: build-icons' `pretty` is set to false as the prettier
// formatter uses babel which I'm not sure it supports typescript
return `
import createLucideIcon from '../createLucideIcon';
import type { AstroComponent } from '../types'
import type { AstroComponent, LucideIconData } from '../types'
const iconData: LucideIconData = ${JSON.stringify(keylessIconData)};
/**
* @component @name ${componentName}
@@ -35,8 +34,9 @@ import type { AstroComponent } from '../types'
* @returns {any} Astro Component
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(keylessChildren)}) as AstroComponent;
const ${componentName} = createLucideIcon(iconData) as AstroComponent;
export default ${componentName};
`;
});
},
);
+28 -16
View File
@@ -1,32 +1,44 @@
---
import defaultAttributes from './defaultAttributes';
import buildLucideIconNode from './utils/buildLucideIconNode';
import type { IconProps as Props } from './types';
import { hasA11yProp } from './utils/hasA11yProp';
const {
color = 'currentColor',
size = 24,
width,
height,
'stroke-width': strokeWidth = 2,
absoluteStrokeWidth = false,
iconNode = [],
nonScalingStroke = false,
iconNode,
icon = {
node: iconNode,
aliases: [],
size: 24,
},
class: className,
...rest
} = Astro.props;
const [, builtIconAttributes, builtIconNode = []] = buildLucideIconNode(icon, {
color,
width: width ?? size,
height: height ?? size,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
className,
hasA11yProp: hasA11yProp(rest),
});
const iconAttributes = {
...builtIconAttributes,
...rest,
};
---
<svg
{...{
...defaultAttributes,
width: size,
height: size,
stroke: color,
'stroke-width': absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
...(!hasA11yProp(rest) && { 'aria-hidden': 'true' }),
...rest,
}}
class:list={['lucide', className]}
>
{iconNode.map(([Tag, attrs]) => <Tag {...attrs} />)}
<svg {...iconAttributes}>
{builtIconNode.map(([Tag, attrs]) => <Tag {...attrs} />)}
<slot />
</svg>
+41 -10
View File
@@ -1,11 +1,43 @@
import type { AstroComponentFactory } from 'astro/runtime/server/render/astro/factory.js';
import type { IconNode } from './types';
import type { LucideIconData, LucideIconNode } from './types';
import { render, renderSlot, createComponent, renderComponent } from 'astro/compiler-runtime';
import Icon from './Icon.astro';
import { mergeClasses } from './utils/mergeClasses';
import { toKebabCase } from './utils/toKebabCase';
export default (iconName: string, iconNode: IconNode): AstroComponentFactory => {
/**
* Creates a Lucide icon component from icon data.
*
* @param icon Icon data object.
* @returns Lucide icon component.
*/
function createLucideIcon(icon: LucideIconData): AstroComponentFactory;
/**
* Creates a Lucide icon component from the legacy icon arguments.
*
* @param iconName Icon name.
* @param iconNode Icon node.
* @param aliases Optional icon aliases.
* @returns Lucide icon component.
*/
function createLucideIcon(
iconName: string,
iconNode: LucideIconNode[],
aliases?: string[],
): AstroComponentFactory;
function createLucideIcon(
iconOrIconName: string | LucideIconData,
iconNode: LucideIconNode[] = [],
aliases: string[] = [],
): AstroComponentFactory {
const icon =
typeof iconOrIconName === 'string'
? {
name: iconOrIconName,
node: iconNode,
aliases,
}
: iconOrIconName;
const Component = createComponent(
($$result, $$props: Record<string, unknown>, $$slots) => {
const { class: className, ...restProps } = $$props;
@@ -14,11 +46,8 @@ export default (iconName: string, iconNode: IconNode): AstroComponentFactory =>
'Icon',
Icon,
{
class: mergeClasses(
Boolean(iconName) && `lucide-${toKebabCase(iconName)}`,
Boolean(className) && className,
),
iconNode,
class: className,
icon,
...restProps,
},
{ default: () => render`${renderSlot($$result, $$slots['default'])}` },
@@ -28,4 +57,6 @@ export default (iconName: string, iconNode: IconNode): AstroComponentFactory =>
'none',
);
return Component;
};
}
export default createLucideIcon;
-15
View File
@@ -1,15 +0,0 @@
import type { SVGAttributes } from './types';
const defaultAttributes: SVGAttributes = {
xmlns: 'http://www.w3.org/2000/svg',
width: 24,
height: 24,
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': 2,
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
};
export default defaultAttributes;
+1 -1
View File
@@ -3,6 +3,6 @@ export * as icons from './icons/index';
export * from './aliases';
export * from './types';
export { default as defaultAttributes } from './defaultAttributes';
export { default as defaultAttributes } from './utils/defaultAttributes';
export { default as createLucideIcon } from './createLucideIcon';
export { default as Icon } from './Icon.astro';
+30 -6
View File
@@ -1,23 +1,47 @@
import type { HTMLAttributes } from 'astro/types';
import type {
LucideIconData as SharedLucideIconData,
LucideIconNode as SharedLucideIconNode,
} from './utils/types';
// Type that the Astro language server needs to infer component props in Astro files
export type AstroComponent = (_props: IconProps) => unknown;
export type AstroComponent = (_props: LucideProps) => unknown;
export interface IconProps extends SVGAttributes {
export type LucideProps = SVGAttributes & {
color?: string;
size?: number | string;
'stroke-width'?: number | string;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
class?: string;
iconNode?: IconNode;
title?: string;
}
};
export type IconProps = LucideProps &
(
| {
icon: LucideIconData;
iconNode?: never;
}
| {
icon?: never;
iconNode: LucideIconNode[];
}
);
export type SVGAttributes = HTMLAttributes<'svg'>;
export type IconNode = IconNodeChild[];
export type LucideIconNode = SharedLucideIconNode<SVGElements, SVGAttributes>;
type IconNodeChild = [elementName: SVGElements, attrs: SVGAttributes];
export type LucideIconData = SharedLucideIconData<SVGElements, SVGAttributes>;
/**
* @deprecated Use LucideIconNode instead.
*/
export type IconNode = LucideIconNode[];
// All possible svg elements according to the Astro definitions
type SVGElements =
@@ -10,8 +10,8 @@ exports[`Using Icon Component > should render icon and match snapshot 1`] = `
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
class="lucide"
aria-hidden="true"
>
<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>
@@ -10,8 +10,8 @@ exports[`Using createLucideIcon > should create a component from an iconNode 1`]
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
class="lucide lucide-air-vent"
aria-hidden="true"
>
<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>
@@ -10,9 +10,9 @@ exports[`Using lucide icon components > should add a non-default attribute to th
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-face-slightly-smiling lucide-smile"
aria-hidden="true"
style="position: absolute"
class="lucide lucide-face-slightly-smiling"
>
<path d="M15 10V9">
</path>
@@ -38,8 +38,8 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
stroke-width="4"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
class="lucide lucide-grid-3x3"
>
<rect width="18"
height="18"
@@ -69,41 +69,50 @@ exports[`Using lucide icon components > should apply all classes to the element
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-droplet lucide-drop my-icon"
aria-hidden="true"
class="lucide lucide-droplet my-icon"
>
<path d="M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z">
</path>
</svg>
`;
exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
exports[`Using lucide icon components > should apply vector-effect when nonScalingStroke is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
width="12"
height="12"
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="1"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
class="lucide lucide-grid-3x3"
>
<rect width="18"
<rect vector-effect="non-scaling-stroke"
width="18"
height="18"
x="3"
y="3"
rx="2"
>
</rect>
<path d="M3 9h18">
<path vector-effect="non-scaling-stroke"
d="M3 9h18"
>
</path>
<path d="M3 15h18">
<path vector-effect="non-scaling-stroke"
d="M3 15h18"
>
</path>
<path d="M9 3v18">
<path vector-effect="non-scaling-stroke"
d="M9 3v18"
>
</path>
<path d="M15 3v18">
<path vector-effect="non-scaling-stroke"
d="M15 3v18"
>
</path>
</svg>
`;
@@ -118,8 +127,8 @@ exports[`Using lucide icon components > should pass children to the icon slot 1`
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-face-slightly-smiling lucide-smile"
aria-hidden="true"
class="lucide lucide-face-slightly-smiling"
>
<path d="M15 10V9">
</path>
@@ -148,8 +157,8 @@ exports[`Using lucide icon components > should render a component 1`] = `
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
class="lucide lucide-grid-3x3"
>
<rect width="18"
height="18"
@@ -179,8 +188,39 @@ exports[`Using lucide icon components > should render the icon with default attr
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
>
</rect>
<path d="M3 9h18">
</path>
<path d="M3 15h18">
</path>
<path d="M9 3v18">
</path>
<path d="M15 3v18">
</path>
</svg>
`;
exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
class="lucide lucide-grid-3x3"
>
<rect width="18"
height="18"
@@ -5,7 +5,10 @@ import { render } from './utils';
describe('Using createLucideIcon', () => {
it('should create a component from an iconNode', async () => {
const AirVent = createLucideIcon('AirVent', airVent);
const AirVent = createLucideIcon({
name: 'air-vent',
node: airVent,
});
const { container } = await render(AirVent);
expect(container.innerHTML).toBeDefined();
+38 -5
View File
@@ -1,7 +1,8 @@
import { describe, it, expect } from 'vitest';
import { Pen, Edit2, Grid, Droplet, Smile, Rocket } from '../src/lucide-astro';
import defaultAttributes from '../src/defaultAttributes';
import { Pen, Edit2, Grid, Droplet, Smile, Rocket, type LucideIconData } from '../src/lucide-astro';
import defaultAttributes from '../src/utils/defaultAttributes';
import { createAstroHTMLString, render } from './utils';
import createLucideIcon from '../src/createLucideIcon.ts';
describe('Using lucide icon components', () => {
it('should render a component', async () => {
@@ -64,11 +65,11 @@ describe('Using lucide icon components', () => {
expect(PenIconContainer.innerHTML).toBe(Edit2Container.innerHTML);
});
it('should not scale the strokeWidth when absoluteStrokeWidth is set', async () => {
it('should scale the strokeWidth when absoluteStrokeWidth is set', async () => {
const { container } = await render(Grid, {
props: {
size: 48,
stroke: 'red',
color: 'red',
absoluteStrokeWidth: true,
},
});
@@ -83,6 +84,24 @@ describe('Using lucide icon components', () => {
expect(container.innerHTML).toMatchSnapshot();
});
it('should apply vector-effect when nonScalingStroke is set', async () => {
const { container } = await render(Grid, {
props: {
size: 12,
nonScalingStroke: true,
},
});
const SVGElement = container.firstElementChild;
expect(SVGElement).toHaveAttribute('width', '12');
expect(SVGElement).toHaveAttribute('height', '12');
expect(SVGElement).toHaveAttribute('stroke-width', '2');
expect(SVGElement?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke');
expect(container.innerHTML).toMatchSnapshot();
});
it('should add a non-default attribute to the element', async () => {
const { container } = await render(Smile, {
props: {
@@ -112,7 +131,20 @@ describe('Using lucide icon components', () => {
});
it('should apply all classes to the element', async () => {
const { container } = await render(Droplet, {
const iconData: LucideIconData = {
name: 'droplet',
size: 24,
node: [
[
'path',
{
d: 'M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z',
},
],
],
aliases: ['drop'],
};
const { container } = await render(createLucideIcon(iconData), {
props: {
class: 'my-icon',
},
@@ -122,6 +154,7 @@ describe('Using lucide icon components', () => {
expect(SVGElement).toHaveClass('my-icon');
expect(SVGElement).toHaveClass('lucide');
expect(SVGElement).toHaveClass('lucide-droplet');
expect(SVGElement).toHaveClass('lucide-drop');
expect(container.innerHTML).toMatchSnapshot();
});
+1
View File
@@ -63,6 +63,7 @@
"@lucide/build-icons": "workspace:*",
"@lucide/helpers": "^1.0.0",
"@lucide/rollup-plugins": "workspace:*",
"@lucide/shared": "workspace:*",
"@rollup/plugin-replace": "^6.0.3",
"@testing-library/jest-dom": "^6.9.1",
"jest-serializer-html": "^7.1.0",
+6 -1
View File
@@ -2,6 +2,11 @@ import plugins from '@lucide/rollup-plugins';
import pkg from './package.json' with { type: 'json' };
import dts from 'rollup-plugin-dts';
const dtsOptions = {
includeExternal: ['@lucide/shared/types'],
tsconfig: './tsconfig.json',
};
const packageName = '@lucide/icons';
const outputFileName = 'lucide-icons';
const inputs = [`src/lucide-icons.ts`];
@@ -81,7 +86,7 @@ export default [
format: 'cjs',
},
],
plugins: [dts()],
plugins: [dts(dtsOptions)],
})),
...configs,
];
+1 -30
View File
@@ -1,30 +1 @@
import { LucideBuildParams, LucideIconData } from './types';
import buildLucideSvg from './buildLucideSvg';
/**
* Creates a base64 encoded data URI from a Lucide icon object.
*
* @param icon The icon to build.
* @param params Additional build parameters.
*/
function buildLucideDataUri(icon: LucideIconData, params: LucideBuildParams = {}) {
const svg = buildLucideSvg(icon, params);
// Browser
if (typeof btoa === 'function') {
// Ensure proper UTF-8 handling before base64
const utf8Bytes = new TextEncoder().encode(svg);
let binary = '';
for (const element of utf8Bytes) binary += String.fromCodePoint(element);
return `data:image/svg+xml;base64,${btoa(binary)}`;
}
// Node.js (and other JS runtimes with Buffer)
if (typeof Buffer !== 'undefined') {
return `data:image/svg+xml;base64,${Buffer.from(svg, 'utf8').toString('base64')}`;
}
throw new Error('No base64 encoder available in this environment.');
}
export default buildLucideDataUri;
export { buildLucideDataUri as default } from '@lucide/shared';
+1 -34
View File
@@ -1,34 +1 @@
import { LucideBuildParams, LucideIconData, LucideIconNode } from './types';
import buildLucideIconNode from './buildLucideIconNode';
const buildDomElement = (
document: Document,
[tagName, attributes, children = []]: LucideIconNode,
): Element => {
const element = document.createElementNS('http://www.w3.org/2000/svg', tagName);
for (const [attrName, value] of Object.entries(attributes)) {
element.setAttribute(attrName, value);
}
for (const node of children) {
const childNode = buildDomElement(document, node);
element.appendChild(childNode);
}
return element;
};
/**
* Creates an SvgElement from a Lucide icon object.
*
* @param document The document to create the Element in.
* @param icon The icon to build.
* @param params Additional build parameters.
*/
function buildLucideIconElement(
document: Document,
icon: LucideIconData,
params: LucideBuildParams = {},
) {
return buildDomElement(document, buildLucideIconNode(icon, params));
}
export default buildLucideIconElement;
export { buildLucideIconElement as default } from '@lucide/shared';
+1 -55
View File
@@ -1,55 +1 @@
import { LucideBuildParams, LucideIconData, LucideIconNode } from './types';
const defaultAttributes = {
xmlns: 'http://www.w3.org/2000/svg',
width: '24',
height: '24',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
};
/**
* Creates a Lucide icon node (an svgson-like format) from a Lucide icon object.
*
* @param icon The icon to build.
* @param params Additional build parameters.
*/
function buildLucideIconNode(icon: LucideIconData, params: LucideBuildParams = {}): LucideIconNode {
const viewBoxWidth = ('size' in icon ? icon.size : icon.width) ?? defaultAttributes.width;
const viewBoxHeight = ('size' in icon ? icon.size : icon.height) ?? defaultAttributes.height;
const aliasClassNames = icon.aliases?.map((alias) => `lucide-${alias}`) ?? [];
const classList = ['lucide', `lucide-${icon.name}`, ...aliasClassNames];
if (params['className']) {
classList.push(params['className']);
}
const attributes = {
...defaultAttributes,
...('color' in params && { stroke: params['color'] }),
...('size' in params &&
params['size'] && {
width: params['size'].toString(10),
height: params['size'].toString(10),
}),
...('width' in params && params['width'] && { width: params['width'].toString(10) }),
...('height' in params && params['height'] && { height: params['height'].toString(10) }),
...('strokeWidth' in params &&
params['strokeWidth'] && { 'stroke-width': params['strokeWidth'].toString(10) }),
class: classList.join(' '),
viewBox: `0 0 ${viewBoxWidth} ${viewBoxHeight}`,
...('attributes' in params && params.attributes),
};
return [
'svg',
attributes,
icon.node.map(([name, attrs]) => [
name,
params['absoluteStrokeWidth'] ? { 'vector-effect': 'non-scaling-stroke', ...attrs } : attrs,
]),
];
}
export default buildLucideIconNode;
export { buildLucideIconNode as default } from '@lucide/shared';
+1 -19
View File
@@ -1,19 +1 @@
import { LucideBuildParams, LucideIconData, LucideIconNode } from './types';
import buildLucideIconNode from './buildLucideIconNode';
const buildDomNode = ([tagName, attributes, children = []]: LucideIconNode): string =>
`<${tagName} ${Object.entries(attributes)
.map(([attrName, value]) => `${attrName}="${value}"`)
.join(' ')}>${children?.map((child) => buildDomNode(child)).join('')}</${tagName}>`;
/**
* Creates an SVG string from a Lucide icon object.
*
* @param icon The icon to build.
* @param params Additional build parameters.
*/
function buildLucideSvg(icon: LucideIconData, params: LucideBuildParams = {}) {
return buildDomNode(buildLucideIconNode(icon, params));
}
export default buildLucideSvg;
export { buildLucideSvg as default } from '@lucide/shared';
+10 -43
View File
@@ -1,43 +1,10 @@
export type SVGProps = Record<string, string>;
/**
* A Lucide icon node (an svgson-like internal format)
*/
export type LucideIconNode =
| [attrName: string, attributes: SVGProps]
| [attrName: string, attributes: SVGProps, children: LucideIconNode[]];
/**
* A Lucide icon object that fully describes an icon to be displayed.
*/
export type LucideIconData = {
name: string;
node: LucideIconNode[];
aliases?: string[];
} & ({ size: number } | { width: number; height: number });
/**
* Build parameters for creating a Lucide icon instance for display.
*/
export type LucideBuildParams = {
/**
* The color of the icon.
*/
color?: string;
/**
* The stroke width.
*/
strokeWidth?: number;
/**
* Adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements.
*/
absoluteStrokeWidth?: boolean;
/**
* Extra CSS class names to pass to the SVG element.
*/
className?: string;
/**
* Any extra attributes to pass to the SVG element.
*/
attributes?: SVGProps;
} & ({ size?: number } | { width?: number; height?: number });
import type {
LucideBuildParams as SharedLucideBuildParams,
LucideIconData as SharedLucideIconData,
LucideIconNode as SharedLucideIconNode,
SVGProps as SharedSvgProps,
} from '@lucide/shared/types';
export type LucideBuildParams = SharedLucideBuildParams;
export type LucideIconData = SharedLucideIconData;
export type LucideIconNode = SharedLucideIconNode;
export type SVGProps = SharedSvgProps;
@@ -1,3 +1,3 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`buildLucideIconNode > should match the snapshot 1`] = `"{"xmlns":"http://www.w3.org/2000/svg","width":"24","height":"24","viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round","class":"lucide lucide-house lucide-home"}"`;
exports[`buildLucideIconNode > should match the snapshot 1`] = `"{"xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round","class":"lucide lucide-house lucide-home"}"`;
@@ -18,24 +18,24 @@ describe('buildLucideIconNode', () => {
it('should override dimensions, but not viewBox', () => {
const HouseSVG = buildLucideIconNode(House, { size: 12 });
expect(HouseSVG[1]['width']).toBe('12');
expect(HouseSVG[1]['height']).toBe('12');
expect(HouseSVG[1]['width']).toBe(12);
expect(HouseSVG[1]['height']).toBe(12);
expect(HouseSVG[1]['viewBox']).toBe('0 0 24 24');
});
it('should override width, but not height', () => {
const HouseSVG = buildLucideIconNode(House, { width: 12 });
expect(HouseSVG[1]['width']).toBe('12');
expect(HouseSVG[1]['height']).toBe('24');
expect(HouseSVG[1]['width']).toBe(12);
expect(HouseSVG[1]['height']).toBe(24);
expect(HouseSVG[1]['viewBox']).toBe('0 0 24 24');
});
it('should override height, but not width', () => {
const HouseSVG = buildLucideIconNode(House, { height: 12 });
expect(HouseSVG[1]['width']).toBe('24');
expect(HouseSVG[1]['height']).toBe('12');
expect(HouseSVG[1]['width']).toBe(24);
expect(HouseSVG[1]['height']).toBe(12);
expect(HouseSVG[1]['viewBox']).toBe('0 0 24 24');
});
@@ -49,11 +49,31 @@ describe('buildLucideIconNode', () => {
it('should override stroke width', () => {
const HouseSVG = buildLucideIconNode(House, { strokeWidth: 12 });
expect(HouseSVG[1]['stroke-width']).toBe('12');
expect(HouseSVG[1]['stroke-width']).toBe(12);
});
it('should scale the stroke width', () => {
const HouseSVG = buildLucideIconNode(House, {
size: 12,
strokeWidth: 2,
absoluteStrokeWidth: true,
});
expect(HouseSVG[1]['stroke-width']).toBe(4);
});
it('should not scale the stroke width', () => {
const HouseSVG = buildLucideIconNode(House, {
size: 12,
strokeWidth: 2,
absoluteStrokeWidth: false,
});
expect(HouseSVG[1]['stroke-width']).toBe(2);
});
it('should set non-scaling-stroke to child nodes', () => {
const HouseSVG = buildLucideIconNode(House, { absoluteStrokeWidth: true });
const HouseSVG = buildLucideIconNode(House, { nonScalingStroke: true });
for (const node of HouseSVG[2]!) {
expect(node[1]['vector-effect']).toBe('non-scaling-stroke');
@@ -61,7 +81,7 @@ describe('buildLucideIconNode', () => {
});
it('should not set non-scaling-stroke', () => {
const HouseSVG = buildLucideIconNode(House, { absoluteStrokeWidth: false });
const HouseSVG = buildLucideIconNode(House, { nonScalingStroke: false });
expect(HouseSVG[1]['vector-effect']).toBeUndefined();
});
@@ -89,4 +109,30 @@ describe('buildLucideIconNode', () => {
expect(HouseSVG[1]['class']).toBe('lucide lucide-house lucide-home icon');
});
it('should support className with multiple classes', () => {
const HouseSVG = buildLucideIconNode(House, {
className: ' icon custom lucide-house ',
});
expect(HouseSVG[1]['class']).toBe('lucide lucide-house lucide-home icon custom');
});
it('should not include default classes when includeDefaultClasses is false', () => {
const HouseSVG = buildLucideIconNode(House, { includeDefaultClasses: false });
expect(HouseSVG[1]['class']).toBeUndefined();
});
it('should add aria-hidden when hasA11yProp is false', () => {
const HouseSVG = buildLucideIconNode(House, { hasA11yProp: false });
expect(HouseSVG[1]['aria-hidden']).toBe('true');
});
it('should not add aria-hidden when hasA11yProp is true', () => {
const HouseSVG = buildLucideIconNode(House, { hasA11yProp: true });
expect(HouseSVG[1]['aria-hidden']).toBeUndefined();
});
});
+5
View File
@@ -8,6 +8,11 @@
"moduleResolution": "node",
"module": "ESNext",
"target": "ESNext",
"baseUrl": ".",
"paths": {
"@lucide/shared": ["../shared/src/index.ts"],
"@lucide/shared/types": ["../shared/src/build/types.ts"],
},
"esModuleInterop": true,
"isolatedModules": true,
"lib": ["esnext", "dom"],
+8 -3
View File
@@ -2,6 +2,11 @@ import plugins from '@lucide/rollup-plugins';
import dts from 'rollup-plugin-dts';
import pkg from './package.json' with { type: 'json' };
const dtsOptions = {
includeExternal: ['@lucide/shared/types'],
tsconfig: './tsconfig.json',
};
const packageName = 'LucidePreact';
const outputFileName = 'lucide-preact';
const outputDir = 'dist';
@@ -58,7 +63,7 @@ export default [
format: 'es',
},
],
plugins: [dts()],
plugins: [dts(dtsOptions)],
},
{
input: `src/${outputFileName}.suffixed.ts`,
@@ -68,7 +73,7 @@ export default [
format: 'es',
},
],
plugins: [dts()],
plugins: [dts(dtsOptions)],
},
{
input: `src/${outputFileName}.prefixed.ts`,
@@ -78,7 +83,7 @@ export default [
format: 'es',
},
],
plugins: [dts()],
plugins: [dts(dtsOptions)],
},
...configs,
];
@@ -1,19 +1,16 @@
import base64SVG from '@lucide/build-icons/utils/base64SVG';
import defineExportTemplate from '@lucide/build-icons/utils/defineExportTemplate';
export default defineExportTemplate(async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
export default defineExportTemplate(
async ({ componentName, iconName, iconData, getSvg, deprecated, deprecationReason }) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `
return `
import createLucideIcon from '../createLucideIcon';
import type { LucideIconData } from '../types';
const iconData: LucideIconData = ${JSON.stringify(iconData)};
/**
* @component @name ${componentName}
@@ -26,8 +23,9 @@ import createLucideIcon from '../createLucideIcon';
* @returns {JSX.Element} JSX Element
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
const ${componentName} = createLucideIcon('${iconName}', ${JSON.stringify(children)});
const ${componentName} = createLucideIcon(iconData);
export default ${componentName};
`;
});
},
);
+41 -28
View File
@@ -1,13 +1,19 @@
import { h, toChildArray } from 'preact';
import defaultAttributes from './defaultAttributes';
import type { IconNode, LucideProps } from './types';
import { buildLucideIconNode, hasA11yProp, mergeClasses } from '@lucide/shared';
import type { LucideIconData, LucideIconNode, LucideProps } from './types';
import { useLucideContext } from './context';
import { mergeClasses } from '@lucide/shared';
import { hasA11yProp } from '@lucide/shared';
interface IconComponentProps extends LucideProps {
iconNode: IconNode;
}
type IconComponentProps = LucideProps &
(
| {
icon: LucideIconData;
iconNode?: never;
}
| {
iconNode: LucideIconNode[];
icon?: never;
}
);
/**
* Lucide icon component
@@ -18,19 +24,28 @@ interface IconComponentProps extends LucideProps {
* @param {number} props.size - The size of the icon
* @param {number} props.strokeWidth - The stroke width of the icon
* @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width
* @param {boolean} props.nonScalingStroke - Whether to use non scaling strokes
* @param {string} props.class - The class name of the icon
* @param {IconNode} props.children - The children of the icon
* @param {IconNode} props.iconNode - The icon node of the icon
* @param {LucideIconNode[]} props.children - The children of the icon
* @param {LucideIconNode[]} props.iconNode - The icon node of the icon
*
* @returns {ForwardRefExoticComponent} LucideIcon
*/
const Icon = ({
color,
size,
width,
height,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
children,
iconNode,
iconNode = [],
icon = {
node: iconNode,
aliases: [],
size: 24,
},
class: classes = '',
...rest
}: IconComponentProps) => {
@@ -38,29 +53,27 @@ const Icon = ({
size: contextSize = 24,
strokeWidth: contextStrokeWidth = 2,
absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
nonScalingStroke: contextNonScalingStroke = false,
color: contextColor = 'currentColor',
class: contextClass = '',
} = useLucideContext() ?? {};
const calculatedStrokeWidth =
absoluteStrokeWidth ?? contextAbsoluteStrokeWidth
? (Number(strokeWidth ?? contextStrokeWidth) * 24) / Number(size ?? contextSize)
: strokeWidth ?? contextStrokeWidth;
const [name, svgAttributes, builtIconNode = []] = buildLucideIconNode(icon, {
color: color ?? contextColor,
width: width ?? size ?? contextSize,
height: height ?? size ?? contextSize,
strokeWidth: strokeWidth ?? contextStrokeWidth,
absoluteStrokeWidth: absoluteStrokeWidth ?? contextAbsoluteStrokeWidth,
nonScalingStroke: nonScalingStroke ?? contextNonScalingStroke,
className: mergeClasses(contextClass, classes as string),
hasA11yProp: Boolean(children) || hasA11yProp(rest),
attributes: rest,
});
return h(
'svg',
{
...defaultAttributes,
width: size ?? contextSize ?? 24,
height: size ?? contextSize ?? 24,
stroke: color ?? contextColor,
['stroke-width' as 'strokeWidth']: calculatedStrokeWidth,
class: mergeClasses('lucide', contextClass, classes),
...(!children && !hasA11yProp(rest) && { 'aria-hidden': 'true' }),
...rest,
},
[...iconNode.map(([tag, attrs]) => h(tag, attrs)), ...toChildArray(children)],
);
return h(name, { ...svgAttributes }, [
...builtIconNode.map(([tag, attrs]) => h(tag, attrs)),
...toChildArray(children),
]);
};
export default Icon;
+12 -1
View File
@@ -5,13 +5,18 @@ const LucideContext = createContext<{
size?: number;
color?: string;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
class?: string;
}>({
size: 24,
color: 'currentColor',
strokeWidth: 2,
absoluteStrokeWidth: false,
nonScalingStroke: false,
class: '',
});
@@ -20,7 +25,11 @@ interface LucideProviderProps {
size?: number;
color?: string;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
class?: string;
}
@@ -30,6 +39,7 @@ export function LucideProvider({
color,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
class: className,
}: LucideProviderProps) {
const value = useMemo(
@@ -38,9 +48,10 @@ export function LucideProvider({
color,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
class: className,
}),
[size, color, strokeWidth, absoluteStrokeWidth, className],
[size, color, strokeWidth, absoluteStrokeWidth, nonScalingStroke, className],
);
return h(LucideContext.Provider, { value }, children);
+39 -17
View File
@@ -1,34 +1,56 @@
import { h, type JSX } from 'preact';
import { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';
import { h, JSX } from 'preact';
import { mergeClasses, toLucideIconData, toPascalCase } from '@lucide/shared';
import Icon from './Icon';
import type { IconNode, LucideIcon, LucideProps } from './types';
import type { LucideIcon, LucideIconData, LucideIconNode, LucideProps } from './types';
/**
* Create a Lucide icon component
* @param {string} iconName
* @param {array} iconNode
* @returns {FunctionComponent} LucideIcon
* Creates a Lucide icon component from icon data.
*
* @param icon Icon data object.
* @returns Lucide icon component.
*/
const createLucideIcon = (iconName: string, iconNode: IconNode): LucideIcon => {
function createLucideIcon(icon: LucideIconData): LucideIcon;
/**
* Creates a Lucide icon component from the legacy icon arguments.
*
* @param iconName Icon name.
* @param iconNode Icon node.
* @param aliases Optional icon aliases.
* @returns Lucide icon component.
*/
function createLucideIcon(
iconName: string,
iconNode: LucideIconNode[],
aliases?: string[],
): LucideIcon;
function createLucideIcon(
iconDataOrName: LucideIconData | string,
iconNode?: LucideIconNode[],
aliases: string[] = [],
): LucideIcon {
const iconData =
typeof iconDataOrName === 'string'
? toLucideIconData(iconDataOrName, iconNode as LucideIconNode[], aliases)
: iconDataOrName;
const Component = ({ class: classes = '', className = '', children, ...props }: LucideProps) =>
h(
Icon,
{
...props,
iconNode,
class: mergeClasses<string | JSX.SignalLike<string | undefined>>(
`lucide-${toKebabCase(toPascalCase(iconName))}`,
`lucide-${toKebabCase(iconName)}`,
classes,
className,
),
icon: iconData,
class: mergeClasses<string | JSX.SignalLike<string | undefined>>(classes, className),
},
children,
);
Component.displayName = toPascalCase(iconName);
if (iconData.name) {
Component.displayName = toPascalCase(iconData.name);
}
return Component;
};
}
export default createLucideIcon;
@@ -1,13 +1 @@
import type { JSX } from 'preact';
export default {
xmlns: 'http://www.w3.org/2000/svg',
width: 24,
height: 24,
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
'stroke-width': '2',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
} satisfies JSX.SVGAttributes;
export { defaultAttributes as default } from '@lucide/shared';
+18 -1
View File
@@ -1,12 +1,29 @@
import { type FunctionComponent, type JSX } from 'preact';
import type {
LucideIconData as SharedLucideIconData,
LucideIconNode as SharedLucideIconNode,
} from '@lucide/shared/types';
export type IconNode = [elementName: keyof JSX.IntrinsicElements, attrs: Record<string, string>][];
export type LucideIconNode = SharedLucideIconNode;
export type LucideIconData = SharedLucideIconData;
/**
* @deprecated Use LucideIconNode instead.
*/
export type IconNode = LucideIconNode[];
export interface LucideProps extends Partial<Omit<JSX.SVGAttributes, 'ref' | 'size'>> {
color?: string;
size?: string | number;
width?: string | number;
height?: string | number;
strokeWidth?: string | number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
}
export type LucideIcon = FunctionComponent<LucideProps>;
@@ -3,7 +3,7 @@
exports[`Using LucideProvider > should render the icon with LucideProvider 1`] = `
<svg
aria-hidden="true"
class="lucide lucide-house"
class="lucide lucide-house lucide-home"
fill="none"
height="48"
stroke="red"
@@ -1,6 +1,6 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`Using createLucideIcon > should create a component from an iconNode 1`] = `
exports[`Using createLucideIcon > should create a component from icon data 1`] = `
<svg
aria-hidden="true"
class="lucide lucide-air-vent"
@@ -29,10 +29,10 @@ 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`] = `
exports[`Using createLucideIcon > should include aliases from icon data 1`] = `
<svg
aria-hidden="true"
class="lucide lucide-air-vent"
class="lucide lucide-circle-check lucide-check-circle"
fill="none"
height="24"
stroke="currentColor"
@@ -58,10 +58,10 @@ exports[`Using createLucideIcon > should create a component from an iconNode wit
</svg>
`;
exports[`Using createLucideIcon > should include backwards compatible className 1`] = `
exports[`Using createLucideIcon > should merge className prop with icon classes 1`] = `
<svg
aria-hidden="true"
class="lucide lucide-layout2 lucide-layout-2"
class="lucide lucide-circle-check lucide-check-circle custom-class"
fill="none"
height="24"
stroke="currentColor"
@@ -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 lucide-grid-3x3"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
>
<rect width="18"
@@ -31,33 +31,42 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
</svg>
`;
exports[`Using lucide icon components > should not scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
exports[`Using lucide icon components > should apply vector-effect when nonScalingStroke is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="1"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid3x3 lucide-grid-3x3"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
>
<rect width="18"
<rect vector-effect="non-scaling-stroke"
width="18"
height="18"
x="3"
y="3"
rx="2"
>
</rect>
<path d="M3 9h18">
<path vector-effect="non-scaling-stroke"
d="M3 9h18"
>
</path>
<path d="M3 15h18">
<path vector-effect="non-scaling-stroke"
d="M3 15h18"
>
</path>
<path d="M9 3v18">
<path vector-effect="non-scaling-stroke"
d="M9 3v18"
>
</path>
<path d="M15 3v18">
<path vector-effect="non-scaling-stroke"
d="M15 3v18"
>
</path>
</svg>
`;
@@ -72,7 +81,38 @@ exports[`Using lucide icon components > should render an component 1`] = `
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid3x3 lucide-grid-3x3"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
>
<rect width="18"
height="18"
x="3"
y="3"
rx="2"
>
</rect>
<path d="M3 9h18">
</path>
<path d="M3 15h18">
</path>
<path d="M9 3v18">
</path>
<path d="M15 3v18">
</path>
</svg>
`;
exports[`Using lucide icon components > should scale the strokeWidth when absoluteStrokeWidth is set 1`] = `
<svg xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewbox="0 0 24 24"
fill="none"
stroke="red"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-grid-3x3 lucide-grid lucide-grid-3-x-3"
aria-hidden="true"
>
<rect width="18"
+20 -1
View File
@@ -51,6 +51,22 @@ describe('Using LucideProvider', () => {
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
it('should render the icon with LucideProvider and custom nonScalingStroke', () => {
const { container } = render(
<LucideProvider
size={48}
color="red"
nonScalingStroke
>
<House />
</LucideProvider>,
);
const IconComponent = container.firstElementChild;
expect(IconComponent?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke');
});
it("should override the provider's global props when passing props to the icon", () => {
const { container } = render(
<LucideProvider
@@ -83,6 +99,9 @@ describe('Using LucideProvider', () => {
const IconComponent = container.firstElementChild;
expect(IconComponent).toHaveAttribute('class', 'lucide provider-class lucide-house icon-class');
expect(IconComponent).toHaveAttribute(
'class',
'lucide lucide-house lucide-home provider-class icon-class',
);
});
});
@@ -4,30 +4,58 @@ import { airVent } from './testIconNodes';
import { render } from '@testing-library/preact';
describe('Using createLucideIcon', () => {
it('should create a component from an iconNode', () => {
it('should create a component from icon data', () => {
const AirVent = createLucideIcon({
name: 'air-vent',
node: airVent,
});
const { container } = render(<AirVent />);
expect(container.firstChild).toMatchSnapshot();
expect(container.firstChild).toBeDefined();
});
it('should include aliases from icon data', () => {
const CircleCheck = createLucideIcon({
name: 'circle-check',
aliases: ['check-circle'],
node: airVent,
});
const { container } = render(<CircleCheck />);
expect(container.firstChild).toMatchSnapshot();
expect(container.firstChild).toBeDefined();
});
it('should merge className prop with icon classes', () => {
const CircleCheck = createLucideIcon({
name: 'circle-check',
aliases: ['check-circle'],
node: airVent,
});
const { container } = render(<CircleCheck class="custom-class" />);
expect(container.firstChild).toMatchSnapshot();
expect(container.firstChild).toBeDefined();
});
it('should create a component from legacy icon arguments', () => {
const AirVent = createLucideIcon('AirVent', airVent);
const { container } = render(<AirVent />);
expect(container.firstChild).toMatchSnapshot();
expect(container.firstChild).toBeDefined();
expect(container.firstChild).toHaveClass('lucide-air-vent');
});
it('should create a component from an iconNode with iconName', () => {
const AirVent = createLucideIcon('air-vent', airVent);
it('should include aliases from legacy icon arguments', () => {
const CircleCheck = createLucideIcon('CircleCheck', airVent, ['check-circle']);
const { container } = render(<AirVent />);
const { container } = render(<CircleCheck />);
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();
expect(container.firstChild).toHaveClass('lucide-circle-check');
expect(container.firstChild).toHaveClass('lucide-check-circle');
});
});
@@ -1,6 +1,6 @@
import { describe, it, expect } from 'vitest';
import { render, cleanup } from '@testing-library/preact';
import { Pen, Edit2, Grid, Droplet } from '../src/lucide-preact';
import { Pen, Edit2, Grid, Droplet, LucideIconData, createLucideIcon } from '../src/lucide-preact';
import defaultAttributes from '../src/defaultAttributes';
type AttributesAssertion = { attributes: Record<string, { value: string }> };
@@ -70,7 +70,7 @@ describe('Using lucide icon components', () => {
expect(PenIconRenderedHTML).toBe(Edit2Container.innerHTML);
});
it('should not scale the strokeWidth when absoluteStrokeWidth is set', () => {
it('should scale the strokeWidth when absoluteStrokeWidth is set', () => {
const { container } = render(
<Grid
size={48}
@@ -89,12 +89,48 @@ describe('Using lucide icon components', () => {
expect(container.innerHTML).toMatchSnapshot();
});
it('should apply vector-effect when nonScalingStroke is set', () => {
const { container } = render(
<Grid
size={48}
stroke="red"
nonScalingStroke
/>,
);
const SVGElement = container.firstElementChild;
expect(SVGElement).toHaveAttribute('stroke', 'red');
expect(SVGElement).toHaveAttribute('width', '48');
expect(SVGElement).toHaveAttribute('height', '48');
expect(SVGElement).toHaveAttribute('stroke-width', '2');
expect(SVGElement?.firstElementChild).toHaveAttribute('vector-effect', 'non-scaling-stroke');
expect(container.innerHTML).toMatchSnapshot();
});
it('should apply all classes to the element', () => {
const iconData: LucideIconData = {
name: 'droplet',
size: 24,
node: [
[
'path',
{
d: 'M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z',
key: 'abc123',
},
],
],
aliases: ['drop'],
};
const testClass = 'my-class';
const { container } = render(<Droplet class={testClass} />);
const Icon = createLucideIcon(iconData);
const { container } = render(<Icon class={testClass} />);
expect(container.firstChild).toHaveClass(testClass);
expect(container.firstChild).toHaveClass('lucide');
expect(container.firstChild).toHaveClass('lucide-droplet');
expect(container.firstChild).toHaveClass('lucide-drop');
});
});
+5
View File
@@ -8,6 +8,11 @@
"moduleResolution": "node",
"module": "ESNext",
"target": "ESNext",
"baseUrl": ".",
"paths": {
"@lucide/shared": ["../shared/src/index.ts"],
"@lucide/shared/types": ["../shared/src/build/types.ts"],
},
"esModuleInterop": true,
"isolatedModules": true,
"lib": ["esnext", "dom"],
@@ -3,6 +3,11 @@ import dts from 'rollup-plugin-dts';
import pkg from './package.json' with { type: 'json' };
import getIconEntryNamesAndAliases from './scripts/getIconEntryNamesAndAliases.mts';
const dtsOptions = {
includeExternal: ['@lucide/shared/types'],
tsconfig: './tsconfig.json',
};
const packageName = 'LucideReact';
const outputFileName = 'lucide-react-native';
const outputDir = 'dist';
@@ -60,7 +65,7 @@ export default [
format: 'es',
},
],
plugins: [dts()],
plugins: [dts(dtsOptions)],
},
{
input: 'src/icons/index.ts',
@@ -70,7 +75,7 @@ export default [
format: 'es',
},
],
plugins: [dts()],
plugins: [dts(dtsOptions)],
},
{
input: iconAndAliasEntries,
@@ -95,7 +100,7 @@ export default [
format: 'es',
},
],
plugins: [dts()],
plugins: [dts(dtsOptions)],
},
{
input: `src/${outputFileName}.prefixed.ts`,
@@ -105,7 +110,7 @@ export default [
format: 'es',
},
],
plugins: [dts()],
plugins: [dts(dtsOptions)],
},
...configs,
];
@@ -1,19 +1,16 @@
import base64SVG from '@lucide/build-icons/utils/base64SVG';
import defineExportTemplate from '@lucide/build-icons/utils/defineExportTemplate';
export default defineExportTemplate(async ({
componentName,
iconName,
children,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
export default defineExportTemplate(
async ({ componentName, iconName, iconData, getSvg, deprecated, deprecationReason }) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `
return `
import createLucideIcon from '../createLucideIcon';
import type { LucideIconData } from '../types';
const iconData: LucideIconData = ${JSON.stringify(iconData)};
/**
* @component @name ${componentName}
@@ -26,8 +23,9 @@ import createLucideIcon from '../createLucideIcon';
* @returns {JSX.Element} JSX Element
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
const ${componentName} = createLucideIcon('${componentName}', ${JSON.stringify(children)});
const ${componentName} = createLucideIcon(iconData);
export default ${componentName};
`;
});
},
);
+66 -21
View File
@@ -1,14 +1,40 @@
import { createElement, forwardRef, type FunctionComponent } from 'react';
import * as NativeSvg from 'react-native-svg';
import defaultAttributes, { childDefaultAttributes } from './defaultAttributes';
import { IconNode, LucideProps } from './types';
import { buildLucideIconForReact, hasA11yProp, mergeClasses } from '@lucide/shared';
import { childDefaultAttributes } from './defaultAttributes';
import { LucideIconData, LucideIconNode, LucideProps } from './types';
import { useLucideContext } from './context';
interface IconComponentProps extends LucideProps {
iconNode: IconNode;
type IconComponentProps = LucideProps & {
testID?: string;
className?: string;
}
} & ({ icon: LucideIconData; iconNode?: never } | { icon?: never; iconNode: LucideIconNode[] });
const toNativeSvgAttrName = (attributeName: string) => {
if (attributeName === 'class') {
return 'className';
}
if (attributeName.startsWith('data-') || attributeName.startsWith('aria-')) {
return attributeName;
}
return attributeName.replace(/-([a-z])/g, (_, character: string) => character.toUpperCase());
};
const toNativeSvgAttributes = (attributes: Record<string, unknown>) => {
return Object.entries(attributes).reduce<Record<string, unknown>>(
(acc, [attributeName, value]) => {
if (value === undefined) {
return acc;
}
acc[toNativeSvgAttrName(attributeName)] = value;
return acc;
},
{},
);
};
/**
* Lucide icon component
@@ -20,8 +46,8 @@ interface IconComponentProps extends LucideProps {
* @param {number} props.strokeWidth - The stroke width of the icon
* @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width
* @param {string} props.className - The class name of the icon
* @param {IconNode} props.children - The children of the icon
* @param {IconNode} props.iconNode - The icon node of the icon
* @param {LucideIconNode[]} props.children - The children of the icon
* @param {LucideIconNode[]} props.iconNode - The icon node of the icon
*
* @returns {ForwardRefExoticComponent} LucideIcon
*/
@@ -30,10 +56,17 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
{
color,
size,
width = size,
height = size,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
children,
iconNode,
iconNode = [],
icon = {
node: iconNode,
size: 24,
},
className,
testID,
...rest
@@ -44,17 +77,28 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
size: contextSize = 24,
strokeWidth: contextStrokeWidth = 2,
absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
nonScalingStroke: contextNonScalingStroke = false,
color: contextColor = 'currentColor',
className: contextClass = '',
} = useLucideContext() ?? {};
const calculatedStrokeWidth =
absoluteStrokeWidth ?? contextAbsoluteStrokeWidth
? (Number(strokeWidth ?? contextStrokeWidth) * 24) / Number(size ?? contextSize)
: strokeWidth ?? contextStrokeWidth;
const hasAccessibleProp = Boolean(children) || hasA11yProp(rest);
const [, svgAttributes, builtIconNode = []] = buildLucideIconForReact(icon, {
color: color ?? contextColor,
width: width ?? size ?? contextSize,
height: height ?? size ?? contextSize,
strokeWidth: strokeWidth ?? contextStrokeWidth,
absoluteStrokeWidth: absoluteStrokeWidth ?? contextAbsoluteStrokeWidth,
nonScalingStroke: nonScalingStroke ?? contextNonScalingStroke,
className: mergeClasses(contextClass, className),
hasA11yProp: hasAccessibleProp,
attributes: rest,
});
const customAttrs = {
stroke: color ?? contextColor ?? defaultAttributes.stroke,
strokeWidth: calculatedStrokeWidth,
stroke: svgAttributes['stroke'],
strokeWidth: svgAttributes['strokeWidth'],
...rest,
};
@@ -62,21 +106,22 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
NativeSvg.Svg as unknown as string,
{
ref,
...defaultAttributes,
width: size ?? contextSize ?? defaultAttributes.width,
height: size ?? contextSize ?? defaultAttributes.height,
...toNativeSvgAttributes(svgAttributes),
'data-testid': testID,
className,
...customAttrs,
...rest,
},
[
...iconNode.map(([tag, attrs]) => {
...builtIconNode.map(([tag, attrs]) => {
const upperCasedTag = (tag.charAt(0).toUpperCase() +
tag.slice(1)) as keyof typeof NativeSvg;
// duplicating the attributes here because generating the OTA update bundles don't inherit the SVG properties from parent (codepush, expo-updates)
return createElement(
NativeSvg[upperCasedTag] as FunctionComponent<LucideProps>,
{ ...childDefaultAttributes, ...customAttrs, ...attrs } as LucideProps,
toNativeSvgAttributes({
...childDefaultAttributes,
...customAttrs,
...attrs,
}) as LucideProps,
);
}),
...((Array.isArray(children) ? children : [children]) || []),
+12 -1
View File
@@ -4,12 +4,17 @@ const LucideContext = createContext<{
size?: number;
color?: string;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
}>({
size: 24,
color: 'currentColor',
strokeWidth: 2,
absoluteStrokeWidth: false,
nonScalingStroke: false,
});
interface LucideProviderProps {
@@ -17,7 +22,11 @@ interface LucideProviderProps {
size?: number;
color?: string;
strokeWidth?: number;
/**
* @deprecated Use `nonScalingStroke` instead.
*/
absoluteStrokeWidth?: boolean;
nonScalingStroke?: boolean;
}
export function LucideProvider({
@@ -26,6 +35,7 @@ export function LucideProvider({
color,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
}: LucideProviderProps) {
const value = useMemo(
() => ({
@@ -33,8 +43,9 @@ export function LucideProvider({
color,
strokeWidth,
absoluteStrokeWidth,
nonScalingStroke,
}),
[size, color, strokeWidth, absoluteStrokeWidth],
[size, color, strokeWidth, absoluteStrokeWidth, nonScalingStroke],
);
return createElement(LucideContext.Provider, { value }, children);

Some files were not shown because too many files have changed in this diff Show More