Any icon works in Bones: every component takes icons as children, so your existing set drops straight in. @usebones/icons adds a typed vocabulary on top: semantic names that fail to compile when misspelled, size from the same tokens as every control, and swap sets in one place. The package is experimental: the vocabulary API may still change between releases while the interface tier settles what it needs from it.

pnpm add @usebones/icons

Use an icon

Names are typed as IconName, so a typo fails at compile time, for people and coding agents alike. Icons render aria-hidden; the control around them carries the accessible name.

import { Icon } from "@usebones/icons";
import { Button } from "@usebones/react";

<Button iconOnly aria-label="Search">
  <Icon name="search" />
</Button>

Grow the vocabulary

Declare your glyphs once and their keys become names: fifty icons is fifty lines in one object plus a single type line, and only the glyphs you import ship. A registered name with no glyph renders nothing and warns once in development.

// icons.tsx
import { defineIcons, IconProvider, type IconNamesOf } from "@usebones/icons";
import { Rocket, ThumbsUp } from "lucide-react";

export const icons = defineIcons({ rocket: Rocket, "thumbs-up": ThumbsUp });

declare module "@usebones/icons" {
  interface IconRegistry extends IconNamesOf<typeof icons> {}
}

<IconProvider icons={icons}>
  <App />
</IconProvider>

<Icon name="rocket" />

Swap the set

The same provider overrides built-in names, so pointing the app at a different set is one object at the root; anything you leave out keeps the default. The toggle in this site's sidebar is exactly that.

<IconProvider icons={{ search: MySearchGlyph, bell: MyBellGlyph }}>
  <App />
</IconProvider>

Built-in names

The names Bones-built UI may ask for, with Lucide defaults; when you swap sets, these are the glyphs to supply. Components hand-roll their own tiny structural glyphs (chevrons, checks), so nothing here is required by a component.

  • align-left
  • align-center
  • align-right
  • arrow-down
  • arrow-left
  • arrow-right
  • arrow-up
  • bell
  • bold
  • check
  • chevron-down
  • chevron-right
  • close
  • copy
  • credit-card
  • info
  • italic
  • loader
  • menu
  • moon
  • search
  • sun
  • underline
  • user

Props

Everything an SVG accepts passes through, plus:

PropTypeDefaultDescription
Icon.nameIconNameWhich glyph to render; a built-in name or one you registered.
Icon.sizenumberPins an exact pixel size; by default the icon follows --ub-icon-size (16px, 14px in compact contexts).
IconProvider.iconsPartial<IconSet>Glyphs by name: overrides for built-in names and the glyphs for names you added; omitted names fall back to the default set.
defineIcons(icons)(icons) => iconsDeclares your glyphs once with their keys preserved as types; pass the result to IconProvider.
IconNamesOf<typeof icons>typeThe registry entries for a defineIcons object; extend IconRegistry with it.

Sizing follows the two-size system described in Sizes.