Skip to content

GyeongHoKim/lucide-lit

Repository files navigation

Lucide Lit

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/

Installation

pnpm add @gyeonghokim/lucide-lit lit
npm install @gyeonghokim/lucide-lit lit
yarn add @gyeonghokim/lucide-lit lit
bun add @gyeonghokim/lucide-lit lit

lit is a peer dependency, so it should be installed by your application.

Quick start

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>

Icon names

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

Customizing 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>
`;

Styling

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.

Accessibility

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".

Bundle behavior

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.

Custom icons

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>

Server-side rendering

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;
}

About

Lucide icons for Web Component, especially for Lit.js

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors