Skip to content
Component installs need the registry setup
VegaStack Design

Icon Picker

Choose a curated Lucide icon or emoji with recents, ranked search and inline colour swatches.

Status
stable
Since
0.23.127
Accessibility pattern
APG dialog (non-modal) picker

Last updated

Install

Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Icon Picker before installation. Retain the digest printed by this command.

pnpm exec vegastack-design verify icon-picker

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/icon-picker

Run the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.

The same command installs the registry items it composes: @vegastack/avatar, @vegastack/button, @vegastack/color-picker, @vegastack/emoji-picker, @vegastack/icon-data, @vegastack/icon-glyph, @vegastack/picker-panel, @vegastack/picker-preferences, @vegastack/popover, @vegastack/tabs, @vegastack/tooltip.

Usage

import { IconPicker } from "@/components/ui/icon-picker";

<IconPicker onValueChange={setIcon} />;

Examples

Composition

Use modes=icon or modes=emoji for one mode without tabs. Both modes use line tabs. Standard grids have eight columns; compact grids have seven. Recents occupy at most two rows. Both tabs share one popover width, sized to the grid, and both browse eight categories: emoji flags are left out of the category bar and reached through search. In icon mode a sticky footer carries the colour swatches on one row — the first, No colour, clears the hue — with Remove as a compact trash icon button (tooltip "Remove") on the right when onRemove is passed; the emoji tab shows only Remove, and the footer is omitted when it has nothing to show. Opening by pointer or keyboard focuses search, so typing filters at once; touch keeps focus on the popup so no on-screen keyboard appears. Clicks inside the popup never bubble to the trigger's ancestors, so the picker can sit in an InputGroupAddon beside a text field. Category headers stay pinned while the grid scrolls. Use closeOnSelect= when editing an identity, and pass onRemove only where removal is supported. Explicit hue, including null, wins over the remembered preference. Give preferenceKey an account/workspace-scoped namespace. Opening never commits a preference. The catalogue has 160 distinct outline icons; labels, pinned upstream tags and reviewed aliases power local ranked search.

API Reference

PropTypeDefaultDescription
onValueChange*(value: IconValue) => void—Called after choosing a glyph.
classNamestring—Additional panel classes.
closeOnSelectbooleantrueClose after choosing a glyph.
disabledbooleanfalseDisable the trigger.
hueAvatarHue | null—Explicit saved hue. Undefined permits a remembered default; null is explicit No colour.
modesreadonly IconPickerMode[]["icon", "emoji"]Enabled modes; one mode omits tabs.
onHueChange((hue: AvatarHue | null) => void)—Called on explicit hue selection or committing a remembered hue with a new icon.
onOpenChange((open: boolean) => void)—Popup state callback.
onRemove(() => void)—Optional Remove action.
openboolean—Controlled popup state.
preferenceKeystring—Namespace for account/workspace-scoped convenience preferences.
refReact.Ref<HTMLButtonElement>—Ref to the trigger.
showRecentsbooleantrueDisplay recent choices.
size"default" | "sm""default"Grid density.
triggerReact.ReactElement<unknown, string | React.JSXElementConstructor<any>>—Custom trigger composed through Base UI.
triggerLabelstring"Choose an icon"Accessible trigger name.
valueIconValue | nullnullChosen icon or emoji.

Data attributes and CSS variables on IconPicker

AttributeValues
data-checkedmirrors a prop or state value
data-sizemirrors a prop or state value
data-slot"icon-picker" | "icon-picker-colours" | "icon-picker-footer" | "icon-picker-swatch" | "icon-picker-tabs"

Accessibility

ContractStates tested
Behaviourdefault, active, disabled, error, pressed, range, selected, success
Accessibilitydisabled, keyboard, labeled, pressed
Visualdefault, hover, error, success

Search is labelled, cells use roving keyboard navigation across visual rows, and result counts use the shared announcer. The footer swatches form one "Icon colour" radiogroup with a single tab stop and arrow-key navigation. Keyboard focus uses the global DS tint; selection uses a persistent 1px primary border — on a grid cell and, rounded, on the checked swatch.

Do / Don't

Do
Compose the shared identity components through supported props.
Don't
Add local visual overrides or entity semantics to the generic glyph.

On this page