<Icon /> wraps a Lucide
SVG via lucide-static. The SVG string is inlined at build time
(no React, no client runtime), tinted with currentColor, and
sized with a small token scale. Kebab-case names match Lucide's icon catalog
directly — see lucide.dev/icons.
Live grid
Twenty common icons at three sizes. Hover any cell to see the name.
search chevron-right chevron-left chevron-down x check info alert-triangle alert-circle check-circle arrow-right arrow-left arrow-up-right mail external-link copy key sparkles shield menu grid.astro | astro
<!-- Three sizes side-by-side for a single name -->
<Icon name="search" size="sm" aria-label="search" />
<Icon name="search" size="md" aria-label="search" />
<Icon name="search" size="lg" aria-label="search" /> Sizes
Five named sizes; pass a number for anything custom.
xs · 12
sm · 16
md · 20
lg · 24
xl · 32
sizes.astro | astro
<Icon name="sparkles" size="xs" /> {/* 12px */}
<Icon name="sparkles" size="sm" /> {/* 16px */}
<Icon name="sparkles" size="md" /> {/* 20px (default) */}
<Icon name="sparkles" size="lg" /> {/* 24px */}
<Icon name="sparkles" size="xl" /> {/* 32px */}
<Icon name="sparkles" size={28} /> {/* arbitrary px */} Stroke weights
Lucide icons are stroked, so weight = stroke-width.
1
1.5
2 (default)
2.5
weights.astro | astro
<Icon name="chevron-right" size="lg" weight={1} />
<Icon name="chevron-right" size="lg" weight={1.5} />
<Icon name="chevron-right" size="lg" weight={2} /> {/* default */}
<Icon name="chevron-right" size="lg" weight={2.5} /> Anatomy
| Property | Behavior |
|---|---|
| Wrapper | Inline `<span>` carrying size / class / aria attributes. |
| SVG | Inlined at build time via lucide-static. `currentColor` for stroke, no fills. |
| Stroke | Width set via `weight` prop; line-caps + joins are round (Lucide defaults). |
Do / Don't
| Property | Do | Don't |
|---|---|---|
| Naming | Use kebab-case lucide names verbatim (e.g. "chevron-right"). | Invent custom names — they will silently render an empty span. |
| aria-label | Pass `aria-label` for icon-only buttons; pair with visible text when text exists. | Use `aria-label` AND surrounding visible text — SR reads both, doubles up. |
| Sizing | Pick a token size ("sm"/"md"/"lg") that matches surrounding text size. | Hardcode arbitrary pixel sizes when a token fits — drifts off the scale. |
| Color | Set `color` on the parent — Icon picks it up via `currentColor`. | Use SVG `fill` overrides — Lucide icons are stroked, not filled. |
| Weight | Stick with weight 2 (default); use 1.5 for marketing surfaces, never below 1. | Mix weights randomly in a single UI — pick one per surface. |
| Decorative use | Omit `aria-label` for purely decorative icons — they get `aria-hidden` automatically. | Set `aria-label=""` — empty labels are an accessibility footgun. |
Props
| Property | Type | Notes | Default |
|---|---|---|---|
| name | string (kebab-case lucide name) — required | "search", "chevron-right" | - |
| size | "xs" | "sm" | "md" | "lg" | "xl" | number | 12 / 16 / 20 / 24 / 32 px | "md" |
| weight | 1 | 1.5 | 2 | 2.5 | maps to SVG stroke-width | 2 |
| class | string | extra classes merged onto the span | - |
| aria-label | string (optional) | when present, role="img" + aria-label; otherwise aria-hidden | - |
Usage
Example.astro | astro
---
import Icon from '@/components/ui/Icon.astro';
---
<!-- decorative (default: aria-hidden) -->
<Icon name="chevron-right" />
<!-- with semantic label (announced by screen readers) -->
<Icon name="alert-triangle" size="lg" aria-label="Warning" />
<!-- explicit size + lighter stroke -->
<Icon name="sparkles" size={28} weight={1.5} class="text-violet-400" /> Accessibility
- Without
aria-label, the icon receivesaria-hidden="true"— use this for icons paired with visible text. - With
aria-label, the icon getsrole="img"and is announced by screen readers — use this for icon-only buttons or status indicators. - Never set both
aria-labelAND surrounding visible text — pick one or the other. - Icons inherit
currentColor, so colour them by settingcoloron the parent.
Source
src/components/ui/Icon.astro — backed by lucide-static.