DhIcon
Inline SVG icon from the Lucide set (lucide.dev) or a custom SVG URL. currentColor tinting or a semantic role variant (info / success / warning / error / primary / neutral), an optional boxed tonal container, and named sizes xs/sm/md/lg plus arbitrary CSS lengths.
Pass any Lucide kebab-case name to <DhIcon name="..." /> — all 1960 ship with
the package — or point src at your own SVG when a glyph isn't in the set. The
examples below cover sizing, accessibility, stroke weight, color, and custom SVGs; the full searchable catalog sits at the bottom of the page.
Sizes
Named class-based sizes `xs`/`sm`/`md`/`lg` produce no inline style attribute (CSP-safe). A number (px) or CSS-length string falls through to a single `--_size` in `style` — set `--dh-icon-size` from your own stylesheet to stay CSP-clean.
<DhIcon name="search" size="xs" />
<DhIcon name="search" size="sm" />
<DhIcon name="search" size="md" /> <!-- default -->
<DhIcon name="search" size="lg" />
<DhIcon name="search" size={40} /> <!-- arbitrary number → px -->
<DhIcon name="search" size="3rem" /> <!-- any CSS length -->Bounding box
The wrapper enforces a square box matching the chosen size. The inner SVG centers on both axes and preserves its aspect ratio — Lucide ships every glyph as a 24×24 viewBox so a non-square render is impossible.
<!-- All four wrappers are perfect squares; the glyph fits inside. -->
<DhIcon name="square" size="lg" />
<DhIcon name="circle" size="lg" />
<DhIcon name="triangle" size="lg" />
<DhIcon name="hexagon" size="lg" />Accessibility
Pass ariaLabel for a meaningful icon (icon-only button, status indicator). Pass decorative=true when the surrounding text already conveys the meaning, so screen readers don't double-announce.
decorative<!-- Labelled: an icon-only button that needs to announce its purpose. -->
<DhIcon name="trash-2" ariaLabel="Delete attachment" />
<!-- Decorative: paired with text that already conveys meaning. -->
<DhButton startIcon="plus" text="Add file" />
<!-- The icon inside the button passes decorative={true} so screen
readers don't announce it on top of "Add file". -->Stroke weight
The default matches Lucide's design at stroke-width 2. Override via --dh-icon-stroke-width on any ancestor to thin or bolden icons in a contained subtree. Use sparingly — drifting stroke weights across the app breaks visual coherence.
<div style="--dh-icon-stroke-width: 1.25">
<DhIcon name="sparkles" />
</div>
<DhIcon name="sparkles" /> <!-- default 2 -->
<div style="--dh-icon-stroke-width: 2.75">
<DhIcon name="sparkles" />
</div>Color via currentColor
<div style="color: var(--color-primary)">
<DhIcon name="plus" />
</div>
<div style="color: var(--color-error)">
<DhIcon name="triangle-alert" />
</div>Role variants
`variant` tints the glyph with a semantic role colour (same token mapping as DhAlert): info / success / warning / error / primary / neutral. Omit it to inherit currentColor.
<DhIcon name="info" variant="info" />
<DhIcon name="circle-check" variant="success" />
<DhIcon name="triangle-alert" variant="warning" />
<DhIcon name="circle-alert" variant="error" />
<DhIcon name="sparkles" variant="primary" />
<DhIcon name="user" variant="neutral" />Boxed
`boxed` wraps the glyph in a tonal container — a 15% role fill, a 1px border at 85%, and the role colour as ink. The same model DhAlert uses; ideal for status chips and feature markers. Without a variant it boxes in the inherited currentColor.
<DhIcon name="info" variant="info" boxed />
<DhIcon name="circle-check" variant="success" boxed />
<DhIcon name="triangle-alert" variant="warning" boxed />
<DhIcon name="circle-alert" variant="error" boxed />
<DhIcon name="sparkles" variant="primary" boxed />
<DhIcon name="user" variant="neutral" boxed />Custom SVG (tinted)
Not every glyph is in Lucide. Pass `src` with a URL to any SVG and it renders in the same context — sized, variant-tinted, and boxable like a Lucide icon. It paints via `mask-image`, so use a monochrome SVG on a transparent background; the fill colour is ignored and the icon takes currentColor / the variant. The URL rides in an inline `--_src` (the same CSP carve-out as a numeric size).
<!-- A monochrome SVG on a transparent background tints like any icon. -->
<DhIcon src="/brand/star.svg" />
<DhIcon src="/brand/star.svg" variant="primary" />
<DhIcon src="/brand/star.svg" size="lg" />
<DhIcon src="/brand/star.svg" boxed variant="success" />Custom SVG (original colours)
For multi-colour marks and logos, add `preserveColor`: the SVG renders as-is through an `<img>`, keeping its own fills, with the box still sized to `size`. It can't be tinted by currentColor or a variant. Provide an `ariaLabel` (or `decorative`) so it has a meaningful accessible name.
<!-- preserveColor keeps the file's own fills — for logos / multi-colour marks. -->
<DhIcon src="/brand/logo.svg" preserveColor ariaLabel="Acme" />
<DhIcon src="/brand/logo.svg" preserveColor size="lg" ariaLabel="Acme" />Import the name list
Use the exported ICON_NAMES array when you need to build pickers or validate an icon name at runtime.
ICON_NAMES.length is 1960.
import { ICON_NAMES } from '@davidhorn/justin';
// ICON_NAMES is a readonly string[] — every value is a valid <DhIcon name="..." />.Catalog
1960 iconsPowered by Lucide. Each
glyph ships as its own static asset and is fetched on demand — your app only pays for the
icons it renders. Click an icon to copy <DhIcon name="..." />.
Showing 200 of 1960. Type to filter the full set — 1760 more matches when you search.