Skip to content

IconPicker ​

Picks an icon from a set of Lucide icons. The trigger looks like a field under a label and shows the chosen icon and its name, or "None". A click opens a popover with an icon grid. Each icon's name is its tooltip and accessible name.

tsx
import { IconPicker } from '@adecore/ui';
tsx
import { useState } from 'react';
import { Bookmark, Bug, Flag, Heart, Rocket, Star, Tag, Zap } from 'lucide-react';
import { IconPicker } from '@adecore/ui';

const ICONS = { Bookmark, Bug, Flag, Heart, Rocket, Star, Tag, Zap };

export default function IconPickerDemo() {
    const [icon, setIcon] = useState<string | null>('Rocket');

    return <IconPicker className="w-72" icons={ICONS} value={icon} onValueChange={setIcon} onClear={() => setIcon(null)} />;
}

Choosing an icon closes the popover and focuses the trigger. The grid is one tab stop. Arrow keys move focus, Home and End jump to the first and last icon, and only Enter, Space or a click chooses.

Pass icons a list of IconPickerGroups, each { id, label, icons } with a translated label. Each group gets a heading with its icon count. Groups add a search field, focused on open; searchable turns it off, or on for a flat set. Escape clears the search first, then closes.

A search matches part of an icon's name, its keywords, or a group's label (keeping the whole group), ignoring case. Empty groups are hidden, and an empty result says so.

With rows, the grid scrolls at that many rows high, so the popover keeps its height during a search. Headings stick to the top, and the chosen icon opens centered.

tsx
import { useState } from 'react';
import {
    Bot,
    Brain,
    Braces,
    Bug,
    Cloud,
    Code,
    Container,
    Cpu,
    Database,
    GitBranch,
    GitMerge,
    Globe,
    HardDrive,
    Laptop,
    Monitor,
    Network,
    Server,
    Smartphone,
    Sparkles,
    Terminal,
    Wand
} from 'lucide-react';
import { IconPicker, type IconPickerGroup } from '@adecore/ui';

const GROUPS: IconPickerGroup[] = [
    { id: 'code', label: 'Code', icons: { code: Code, braces: Braces, terminal: Terminal, bug: Bug, 'git-branch': GitBranch, 'git-merge': GitMerge } },
    { id: 'ai', label: 'AI', icons: { bot: Bot, brain: Brain, sparkles: Sparkles, wand: Wand } },
    { id: 'infra', label: 'Infrastructure', icons: { server: Server, cloud: Cloud, database: Database, container: Container, network: Network, globe: Globe } },
    { id: 'hardware', label: 'Hardware', icons: { cpu: Cpu, 'hard-drive': HardDrive, laptop: Laptop, monitor: Monitor, smartphone: Smartphone } }
];

const KEYWORDS = { container: ['docker'], bot: ['agent'], database: ['sql'] };

export default function IconPickerGroupsDemo() {
    const [icon, setIcon] = useState<string | null>('database');

    return <IconPicker className="w-72" icons={GROUPS} keywords={KEYWORDS} rows={4} value={icon} onValueChange={setIcon} />;
}

With onClear, a "No icon" button under the grid clears the icon. Leave it out when the thing has a default icon. The label defaults to "Icon" (iconPicker.label).

Props ​

PropTypeDefault
iconsReadonly<Record<string, LucideIcon>> | readonly IconPickerGroup[]Required. Every icon by its name, in the order of the grid, or in groups.
valuestring | nullRequired. The name of the picked icon.
onValueChange(name: string) => voidRequired.
keywordsReadonly<Record<string, readonly string[]>>More words a search finds an icon by, by icon name.
searchablebooleantrue for groupsA search field above the grid.
rowsnumberThe height of the scroll area in rows. Without it the grid is as tall as its icons.
onClear() => voidOffers "No icon" under the grid, and a clear button beside the trigger while an icon is chosen.
labelstring"Icon"The label above the trigger.
hideLabelbooleanfalseKeeps the label for assistive technology only.
disabledbooleanfalseThe trigger does not open.
classNamestringOn the box around the label and the trigger.
refRef<HTMLDivElement>The same box.

IconPickerProps and IconPickerGroup are exported types.

Licenses and third-party notices are listed in each package.