You're in the Library

Icon

Draws a Lucide icon at a token size with the system stroke width, in the color of the text around it. Decorative by default: pass a label only when the icon carries meaning on its own.
Stable
Edit

Preview appears after the next Storybook update.

Icon example
<Icon />
Rendered with the real componentFrom the code
Light
Dark

When to use

Use it for

  • Next to a label, to help people scan: a menu item, a tab, a status line, a table cell.
  • Inside your own controls, at size sm (16), the size the system uses in controls.
  • In navigation at size md (20), so the side nav reads at a glance.
  • Any Lucide glyph: pass the component itself, for example icon={GitBranch} imported from lucide-react.

Not for

  • As the only content of a button: use IconButton, which requires a label and sizes the hit area.
  • Inside Button or Badge when they place the icon for you: Button takes iconStart and iconEnd, Badge takes icon.
  • Emoji or Unicode glyphs standing in for icons: the system uses one family, Lucide.
  • Illustrations or logos: Icon draws 24px grid line icons only.

Do and don't

Every example is rendered with the real component, so it always matches what ships.

Open pull request
Do

Pair the icon with text

The word carries the meaning and the icon helps people find it. The icon stays hidden from screen readers, so the label is read once.

Do

Pick the size from the token scale

xs 12, sm 16, md 20, lg 24. The stroke stays at icon.stroke on every size, so icons keep the weight of the text.

Do

Give a label when the icon stands alone and means something

With label, Icon becomes role="img" with that accessible name. Say what it means ("Merged"), not what it shows ("Purple circle").

Don't

Set a color or a size by hand

Icon inherits currentColor and takes its size from the icon size tokens. A hard-coded width or color drifts from the theme and the density.

Don't

Rely on the icon alone to say a status

Color and shape are not enough for everyone. Put the status in words next to it, or use Badge.

Accessibility

  • Without label, the icon gets aria-hidden="true": screen readers skip it and read the text next to it.
  • With label, it gets role="img" and aria-label, so it is announced as an image with that name.
  • focusable="false" keeps the SVG out of the tab order. Make the parent control focusable, never the icon.
  • The icon inherits the text color, so it gets the same contrast as the label it sits next to.

Content guidelines

  • A label names the meaning in sentence case: "Merged", "Opens in a new tab".
  • Do not add "icon" or "image" to the label: screen readers already announce the role.

From the code Generated from the component, not editable here.

Anatomy and tokens

Read from packages/react/src/components/Icon/Icon.module.css at build time. Every value is a token: open one to see its alias chain in both themes.

5 tokens5 semantic or component, change with theme and brand0 from the shared scales

Motion and interaction

Icon does not animate.

From the code Generated from the component, not editable here.

Props

Parsed from the TypeScript types in packages/react/src/components/Icon/Icon.tsx, so this table cannot drift from the code.

PropTypeDefaultDescription
iconrequiredLucideIconnoneA Lucide icon component, for example `import { GitBranch } from 'lucide-react'`.
size'xs' | 'sm' | 'md' | 'lg''sm'Token size: xs 12, sm 16, md 20, lg 24. Default `sm`, the size used inside controls.
labelstringnoneAccessible name. Omit for decorative icons next to text (they are hidden from assistive tech).

Playground

Change the props and see the real Icon update. Controls are generated from the TypeScript props; nothing here changes the system.

Code for these props
<Icon size="xs" />

size

Workbench

For contributors: every state with the accessibility panel, in the component workbench (Storybook). The stories double as visual test fixtures; the guidance on this page is the user documentation (open Icon in Storybook).

Embed Storybook stories here (4)

Loads from https://bridge-storybook.floriangouloubi.com (set STORYBOOK_URL at build time). If Storybook is not running, the frames stay empty.

Default

Large

WithLabel

AllStates

From the code Generated from the component, not editable here.

Changelog

  • New

    Missing pieces now include a library component restyled from outside with tokens only: CSS on a class the component carries, often on a state selector such as [aria-pressed='true'], or a token-only inline style tied to its pressed prop, reads "IconButton: restyled when pressed" with what changed (fill (icon) currentColor) and a restyle mark that sets it apart from hand-picked values

    v1.0.0, Nov 11, 2026

  • New

    Component specs describe swappable parts: swap on icon parts (default icon from the docs preview or the AllStates story) and on full-bleed compound content (Card body: text or media slot), slots per variant, and file-level icons (normalized Lucide SVGs read from lucide-react), iconStyle and slots sections

    v1.0.0, Nov 11, 2026

  • Fix

    Measured only: a frame bigger than its children plus its padding on an axis its parent does not size keeps its measured size (a 48 px button around a 16 px icon, a 24 px label row around a 16 px label), read from the boxes when the measure did not mark it as sized

    v1.0.0, Nov 11, 2026

  • Fix

    Figma plugin: updating a component no longer adds its icon swap property again (Badge showed Icon2 and Icon3 after Update all)

    v1.0.0, Nov 11, 2026

  • Fix

    Measured only, for any design system pulled from its Storybook: a part the measure gives as SVG (MeasuredSpec.vectors, an icon, a check mark) is drawn as vectors instead of an empty frame, and a positioned part (a pseudo-element knob) follows its offset in each variant

    v1.0.0, Nov 11, 2026

  • Fix

    Figma plugin: the BOOLEAN of a swappable part is named "Show icon" ("Show icon start"), as Figma kits do, so the swap keeps the plain name "Icon" instead of the "Icon2" Figma gave it

    v1.0.0, Nov 11, 2026

  • Fix

    The Bridge connector shows as Bridge with the Bridge mark: the MCP server's initialize answer carries title: "Bridge", websiteUrl and icons (MCP 2025-11-25 Implementation, PNG 192 and 512 plus the SVG), and the site serves favicon.ico (16, 32, 48), apple-touch-icon.png, icon-192.png, icon-512.png and a site.webmanifest named Bridge, linked from every page head

    v1.0.0, Nov 11, 2026

  • Fix

    Usage guidance for Icon

    v1.0.0, Nov 11, 2026

  • Fix

    Usage guidance for Icon and VisuallyHidden, drafted by Claude from their code and marked for review

    v1.0.0, Nov 11, 2026

  • Fix

    Regenerates component-specs.json with icon and content slot swaps and the icons section

    v1.0.0, Nov 11, 2026

  • Fix

    Being built: a library request for a component the library has now shows that component's real Library preview in List, Grid and Board (the same stage as Library > Components cards, now one shared component), instead of the generic component icon

    v1.0.0, Nov 11, 2026

All release notes