Lucide icons as Lit custom elements.
Each icon is registered as a web component, so you can use Lucide icons in Lit
templates as regular HTML tags such as <lucide-camera>,
<lucide-circle-alert>, and <lucide-search>.
Try the interactive demo: https://gyeonghokim.github.io/lucide-lit/
pnpm add @gyeonghokim/lucide-lit litnpm install @gyeonghokim/lucide-lit lityarn add @gyeonghokim/lucide-lit litbun add @gyeonghokim/lucide-lit litlit is a peer dependency, so it should be installed by your application.
Import the icons you want to use. Importing an icon registers its custom element.
import { LitElement, html } from 'lit';
import { Camera } from '@gyeonghokim/lucide-lit';
// Keep the imported icon referenced so bundlers and linters do not remove it.
void Camera;
class UploadButton extends LitElement {
render() {
return html`
<button>
<lucide-camera size="20" aria-hidden="true"></lucide-camera>
Upload
</button>
`;
}
}
customElements.define('upload-button', UploadButton);You can also import an icon once in an application entry file and use its tag in any Lit template loaded after that import.
import { Search } from '@gyeonghokim/lucide-lit';
void Search;<lucide-search></lucide-search>Lucide icon component names map to kebab-case custom element tags:
| Import name | Custom element |
|---|---|
Camera |
<lucide-camera> |
CircleAlert |
<lucide-circle-alert> |
PanelLeftOpen |
<lucide-panel-left-open> |
Browse the available icon names in the Lucide icon directory: https://lucide.dev/icons
Icons use Lucide's default SVG styling: size="24", color="currentColor",
stroke-width="2", fill="none", and rounded stroke caps and joins.
<lucide-camera
size="32"
color="tomato"
stroke-width="1.5"
></lucide-camera>Supported attributes:
| Attribute | Type | Default | Description |
|---|---|---|---|
size |
number | 24 |
Sets both SVG width and height. |
color |
string | currentColor |
Sets the SVG stroke color. |
stroke-width |
number | 2 |
Sets the SVG stroke width. |
absolute-stroke-width |
boolean | false |
Keeps the visual stroke width consistent when size changes. |
class |
string | none | Adds classes to the host and the rendered SVG. |
aria-hidden |
string | automatic | Controls whether assistive technology ignores the icon. |
aria-label |
string | none | Labels a meaningful icon. |
title |
string | none | Adds a title attribute to the rendered SVG. |
role |
string | none | Sets the rendered SVG role. |
Use Lit property or boolean attribute bindings when values are dynamic:
html`
<lucide-camera
.size=${32}
.color=${'tomato'}
.strokeWidth=${1.5}
.absoluteStrokeWidth=${true}
></lucide-camera>
`;html`
<lucide-camera
size=${this.iconSize}
?absolute-stroke-width=${this.keepStrokeWidth}
></lucide-camera>
`;Because the default stroke color is currentColor, icons usually inherit color
from the surrounding text or from the host element.
button {
color: rebeccapurple;
}
lucide-camera {
color: var(--accent-color);
}<button>
<lucide-camera size="18" aria-hidden="true"></lucide-camera>
Take photo
</button>The SVG is rendered inside the custom element's shadow root. Prefer the public attributes above for size, color, stroke width, and accessibility rather than styling internal SVG selectors from outside the component.
Decorative icons should be hidden from assistive technology:
<lucide-camera aria-hidden="true"></lucide-camera>Meaningful icons should have an accessible name:
<lucide-camera role="img" aria-label="Open camera"></lucide-camera>If you do not pass aria-label, title, role, aria-hidden, or slotted
content, the icon defaults to aria-hidden="true".
The package currently exposes a single public entry point:
import { Camera, Search } from '@gyeonghokim/lucide-lit';Prefer named imports for the icons your component actually renders. Avoid namespace imports unless you intentionally need to reference many icons:
import * as lucideIcons from '@gyeonghokim/lucide-lit';If an imported icon is only used through its custom element tag, keep the import
referenced with void IconName as shown in the quick start. That makes the
registration side effect explicit to TypeScript, linters, and bundlers.
Use createLucideIcon when you want to register an icon from a Lucide-style
icon node.
import { createLucideIcon } from '@gyeonghokim/lucide-lit';
export const BrandMark = createLucideIcon('BrandMark', [
['path', { d: 'M4 4h16v16H4z' }],
['path', { d: 'M8 8h8v8H8z' }],
]);<lucide-brand-mark></lucide-brand-mark>Icon modules register custom elements when they are imported, so they need a
browser environment with customElements.
For SSR frameworks, import icons from client-only modules or load them dynamically in the browser:
if (typeof window !== 'undefined') {
const { Camera } = await import('@gyeonghokim/lucide-lit');
void Camera;
}