Icon / emoji picker

A popover picker for the full Unicode emoji set (~1,900 via `unicode-emoji-json`) and every lucide icon (~1,900 via `lucide-react/dynamic`). Pass `mode` to offer emojis, icons, or both — `both` shows a rounded Emoji / Icons tab strip whose Search tab morphs into a filter field. Both grids are windowed (only the visible rows mount) and icons load lazily, so showing everything stays fast and the consumer's bundle stays light. Built from Squad UI components (Popover, SearchTabs, TextField, Button). Fully keyboard-driven while open: Shift+←/→ switches tabs, arrow keys roam the grid, and Enter picks. Picking does not close the popover; the always-present Remove button clears the selection. Selection is `{ type, value }` where `value` is the emoji character or the lucide icon name.

Installation
$Terminal
npx shadcn@latest add "https://raw.githubusercontent.com/sis-thesqd/squad-sdk/main/public/r/icon-emoji-picker.json"
Usage
Import the flat block (one line) and drop it in — it owns the popover, the search, and the selection state. The CLI install auto-adds the npm dependencies; for a manual copy run the install command below.
$Dependencies
npm install lucide-react unicode-emoji-json
TSImport
import { IconEmojiPicker, type IconEmojiValue } from "@/components/blocks/icon-emoji-picker";
TSExample
"use client";

import * as React from "react";
import { IconEmojiPicker, type IconEmojiValue } from "@/components/blocks/icon-emoji-picker";

export function Example() {
  const [value, setValue] = React.useState<IconEmojiValue | null>(null);
  return <IconEmojiPicker value={value} onChange={setValue} />;
}
Composition
Props exposed by the block.
IconEmojiPicker            (single flat block — one-line import)
├── mode="emoji | icon | both"   (both = tab toggle, default)
├── value / defaultValue / onChange   ({ type: "emoji" | "icon"; value })
├── trigger={<custom />}        (defaults to a square button showing the pick)
├── side / align               (popover placement)
└── disabled

Helper: <LucideByName name="rocket" /> renders a lucide icon by kebab name.
Default (both)
`mode="both"` (default) shows a rounded Emoji / Icons tab strip with a morphing Search tab. Switch tabs with Shift+←/→, roam the grid with arrow keys, and press Enter to pick.
Emoji only
`mode="emoji"` hides the tabs and shows only the emoji catalog — the full Unicode set, windowed so it scrolls smoothly. Filter by name or keyword.
Icons only
`mode="icon"` shows only the lucide catalog — every icon, loaded lazily and windowed so the bundle stays light. Icons are addressed by their kebab-case name; search matches the name.
API Reference
Props exposed by the IconEmojiPicker component.
PropTypeDefaultDescription
mode"emoji" | "icon" | "both""both"Which catalogs to offer. `both` shows the rounded Emoji / Icons tab strip with a morphing search; a single mode hides the tabs (just the search field).
value{ type: "emoji" | "icon"; value: string } | nullControlled selection. `value` is the emoji character or the kebab-case lucide icon name. Pass with `onChange`.
defaultValueIconEmojiValue | nullnullUncontrolled initial selection.
onChange(value: IconEmojiValue) => voidFired when a glyph is picked; the popover then closes.
triggerReact.ReactNodeCustom trigger element. Defaults to a square button showing the current pick (or a placeholder).
side"top" | "right" | "bottom" | "left""bottom"Popover placement side.
align"start" | "center" | "end""start"Popover alignment.
disabledbooleanDisable the default trigger.