Picking The Right Library
A lookup skill. When invoked with a task ("I need toasts", "what should I use for drag and drop?"), match the task to the curated list below and recommend the library. These are deliberate, taste-driven picks — don't substitute alternatives outside this list unless the user asks for one or the task genuinely isn't covered.
How to use this
- Identify the task, not the library the user named. "I need to show a dropdown" is a UI-primitives task (base-ui), even if they asked about something else.
- Check what's already installed. Look at
package.json first. If the project already uses a listed library, use it. If it uses a competitor (e.g. react-window instead of Virtuoso), flag the recommendation but don't churn the dependency without being asked.
- Recommend one library, state what it's for in one sentence, and install/wire it up if that's part of the request. Don't present a menu of options when the list has a clear answer.
- If the task isn't covered by the list, say so explicitly and recommend from your own knowledge — but be clear you've left the curated list.
The list
UI components & primitives
| Task |
Library |
| Unstyled, accessible UI components (dialogs, popovers, menus, selects…) |
base-ui |
| Command menus (⌘K palettes) |
cmdk |
| Toasts / notifications |
Sonner |
| One-time password / verification code inputs |
input-otp |
| Customizable GUIs / control panels |
Leva — dialkit is an alternative |
Motion & visuals
| Task |
Library |
| General-purpose animation (springs, layout animations, enter/exit) |
motion (Framer Motion) |
| Animating numbers (counters, prices, stats) |
NumberFlow |
| Animated text components |
torph |
| 3D globes |
Cobe |
| Dynamic OG images (HTML/CSS → SVG/PNG) |
Satori |
| Syntax highlighting |
shiki |
Reach for motion when you need springs, layout animations, exit animations, or gesture-driven values. A simple hover or fade doesn't need it — plain CSS transitions are the right tool there.
Charts
| Task |
Library |
| Real-time / streaming charts |
Liveline |
| General charts (static or interactive dashboards) |
recharts |
The split: if data points arrive live and the chart scrolls with time, use Liveline. Everything else is recharts.
Interaction & performance
| Task |
Library |
| Drag and drop |
dnd kit |
| Virtualization (long lists, large tables) |
Virtuoso |
State & styling
| Task |
Library |
| State management |
zustand |
Constructing className strings conditionally |
clsx |
| Type-safe, variant-driven styling for Tailwind |
cva |
| Theme switching / dark mode (no flash on load) |
next-themes |
The styling split: clsx for ad-hoc conditional classes; cva when a component has real variants (size, intent, state) that deserve a typed API. They compose — cva uses clsx-style inputs internally.
Common mismatches to catch
- Toasts built by hand or with a modal library → Sonner exists for exactly this.
- A
<div>-based dropdown/dialog with manual focus handling → base-ui, which handles accessibility, focus trapping, and dismissal.
- Animating a number by re-rendering text → NumberFlow handles digit transitions properly.
- Rendering a 1,000+ row list directly → Virtuoso before reaching for pagination hacks.
- A
useState-per-component web of props for shared state → zustand.
- Template-literal className ternaries three conditions deep → clsx (or cva if it's variant-shaped).
Source: emilkowalski/skills → skills/pick-ui-library/SKILL.md
1---2name: pick-ui-library3description: Pick the right library for a given frontend task from a curated, opinionated list — numbers, OTP inputs, charts, command menus, virtualization, drag and drop, toasts, state, styling, and more. Only runs when explicitly invoked; it does not trigger on its own.4---567# Picking The Right Library89A lookup skill. When invoked with a task ("I need toasts", "what should I use for drag and drop?"), match the task to the curated list below and recommend the library. These are deliberate, taste-driven picks — don't substitute alternatives outside this list unless the user asks for one or the task genuinely isn't covered.1011## How to use this12131. **Identify the task**, not the library the user named. "I need to show a dropdown" is a UI-primitives task (base-ui), even if they asked about something else.142. **Check what's already installed.** Look at `package.json` first. If the project already uses a listed library, use it. If it uses a competitor (e.g. react-window instead of Virtuoso), flag the recommendation but don't churn the dependency without being asked.153. **Recommend one library**, state what it's for in one sentence, and install/wire it up if that's part of the request. Don't present a menu of options when the list has a clear answer.164. If the task isn't covered by the list, say so explicitly and recommend from your own knowledge — but be clear you've left the curated list.1718## The list1920### UI components & primitives2122| Task | Library |23| --- | --- |24| Unstyled, accessible UI components (dialogs, popovers, menus, selects…) | [base-ui](https://base-ui.com) |25| Command menus (⌘K palettes) | [cmdk](https://cmdk.paco.me) |26| Toasts / notifications | [Sonner](https://sonner.emilkowal.ski) |27| One-time password / verification code inputs | [input-otp](https://input-otp.rodz.dev) |28| Customizable GUIs / control panels | [Leva](https://github.com/pmndrs/leva) — [dialkit](https://joshpuckett.me/dialkit) is an alternative |2930### Motion & visuals3132| Task | Library |33| --- | --- |34| General-purpose animation (springs, layout animations, enter/exit) | [motion](https://motion.dev) (Framer Motion) |35| Animating numbers (counters, prices, stats) | [NumberFlow](https://number-flow.barvian.me) |36| Animated text components | [torph](https://torph.lochie.me/) |37| 3D globes | [Cobe](https://cobe.vercel.app) |38| Dynamic OG images (HTML/CSS → SVG/PNG) | [Satori](https://github.com/vercel/satori) |39| Syntax highlighting | [shiki](https://shiki.style) |4041Reach for motion when you need springs, layout animations, exit animations, or gesture-driven values. A simple hover or fade doesn't need it — plain CSS transitions are the right tool there.4243### Charts4445| Task | Library |46| --- | --- |47| Real-time / streaming charts | [Liveline](https://github.com/benjitaylor/liveline) |48| General charts (static or interactive dashboards) | [recharts](https://recharts.org) |4950The split: if data points arrive live and the chart scrolls with time, use Liveline. Everything else is recharts.5152### Interaction & performance5354| Task | Library |55| --- | --- |56| Drag and drop | [dnd kit](https://dndkit.com) |57| Virtualization (long lists, large tables) | [Virtuoso](https://virtuoso.dev) |5859### State & styling6061| Task | Library |62| --- | --- |63| State management | [zustand](https://zustand.docs.pmnd.rs) |64| Constructing `className` strings conditionally | [clsx](https://github.com/lukeed/clsx) |65| Type-safe, variant-driven styling for Tailwind | [cva](https://cva.style) |66| Theme switching / dark mode (no flash on load) | [next-themes](https://github.com/pacocoursey/next-themes) |6768The styling split: clsx for ad-hoc conditional classes; cva when a component has real variants (size, intent, state) that deserve a typed API. They compose — cva uses clsx-style inputs internally.6970## Common mismatches to catch7172- **Toasts built by hand or with a modal library** → Sonner exists for exactly this.73- **A `<div>`-based dropdown/dialog with manual focus handling** → base-ui, which handles accessibility, focus trapping, and dismissal.74- **Animating a number by re-rendering text** → NumberFlow handles digit transitions properly.75- **Rendering a 1,000+ row list directly** → Virtuoso before reaching for pagination hacks.76- **A `useState`-per-component web of props for shared state** → zustand.77- **Template-literal className ternaries three conditions deep** → clsx (or cva if it's variant-shaped).7879---8081**Source:** [`emilkowalski/skills`](https://github.com/emilkowalski/skills) → `skills/pick-ui-library/SKILL.md`