Skip to content

IconPicker ​

The mark a thing wears, picked from a set of Lucide icons, as a grid under a label. Every icon's name is its tooltip and its accessible name. The grid is one tab stop: the arrow keys move the focus, across groups by what is drawn above and below, Home and End go to the first and the last icon, and only Enter, Space or a click chooses, so a surface that saves every choice at once never saves one the arrows passed.

tsx
import { IconPicker } from '@basmilius/desktop-ui';
tsx
import { useState } from 'react';
import { Bookmark, Bug, Flag, Heart, Rocket, Star, Tag, Zap } from 'lucide-react';
import { IconPicker } from '@basmilius/desktop-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)} />;
}

A long set reads better in groups. Hand icons a list of IconPickerGroups, each { id, label, icons } with its label already in the reader's language, and every group gets a heading with the number of icons it shows. Groups bring a search field, which searchable turns off or, for a flat set, on. The search and the grid then share one frame, the frame wears the focus outline while the search has it, and Escape empties a search that holds text before it reaches a dialog.

A search matches a part of an icon's name, of its group's label, or of a word in keywords for that icon, whatever the case. A group without a match goes; nothing at all says so in the grid.

With rows, the grid is a scroll area that many rows high, plus room for one heading, so the frame keeps its height through a search. The headings stick to its top, and the area opens with the chosen icon in the middle.

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 '@basmilius/desktop-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 button beside the label clears the icon. Leave it out when the thing falls back to a default icon of its own. The label defaults to the library's "Icon"; give it another name when the same surface also takes a picture, or the two read as one list.

Props ​

PropType
iconsReadonly<Record<string, LucideIcon>> | readonly IconPickerGroup[]Required. Every icon by its name, in the order of the grid, or in groups.
keywordsReadonly<Record<string, readonly string[]>>More words a search finds an icon by, by icon name.
searchablebooleanA search field above the grid. On for groups, off for a flat set.
rowsnumberThe height of the scroll area in rows. Without it the grid is as tall as its icons.
valuestring | nullRequired. The name of the picked icon.
onValueChange(name: string) => voidRequired.
onClear() => void
labelstring
disabledboolean
classNamestring
refRef<HTMLDivElement>

IconPickerProps and IconPickerGroup are exported types.