agent-elements — Claude Code skill: what it does, how to install
Guides agents in building chat, tool-calling, and approval UIs in React 19 and Tailwind v4 projects. Uses shadcn components and the Vercel AI SDK.
npx skills add 21st-dev/agent-elements --skill agent-elements -a claude-codeWhat agent-elements does
This skill injects context for the Agent Elements shadcn component collection. It teaches coding assistants how to assemble agent chat screens, render tool calls, and manage streaming states using the Vercel AI SDK. Assistants work with prebuilt parts like AgentChat, InputBar, and specialized cards such as BashTool and EditTool.
Files land under @/components/agent-elements/. The skill enforces direct file imports because barrel files do not exist here. Your project must run on React 19 and Tailwind v4, with ai and @tabler/icons-react installed.
Loading this context for a bare-bones chat with plain text messages is a mistake. The setup specifically targets tool-calling workflows, diff inspections, and step approvals.
What it helps with
- Render command-line output and structured code diffs inside agent chats
- Set up approval cards for plans and file modifications
- Build an input bar with mode selectors and prompt suggestion chips
- Integrate prebuilt chat components with the
useChathook from the AI SDK
Example prompts for the agent
- Build an agent chat interface using AgentChat and register BashTool with EditTool
- Add an InputBar with model selection chips and action slots
- Create a plan approval card using PlanTool connected to useChat
How to install agent-elements
1Skills CLI (Vercel)
npx skills add 21st-dev/agent-elements --skill agent-elements -a claude-code2Manually: put the files in place
~/.claude/skills/agent-elements/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/agent-elements/
1Skills CLI (Vercel)
npx skills add 21st-dev/agent-elements --skill agent-elements -a codex2Manually: put the files in place
.agents/skills/agent-elements/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add 21st-dev/agent-elements --skill agent-elements -a cursor2Manually: put the files in place
.cursor/skills/agent-elements/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install 21st-dev/agent-elements agent-elements2Manually: put the files in place
.github/skills/agent-elements/SKILL.md1Skills CLI (Vercel)
npx skills add 21st-dev/agent-elements --skill agent-elements -a gemini-cli2Platform CLI
gemini skills install https://github.com/21st-dev/agent-elements --path skills/agent-elementsManually: put the files in place
.devin/skills/agent-elements/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add 21st-dev/agent-elements --skill agent-elements -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about agent-elements
How do I install agent-elements in Claude Code?
Run npx skills add 21st-dev/agent-elements --skill agent-elements -a claude-code. The skill file will be placed in ~/.claude/skills/agent-elements/SKILL.md.
What does agent-elements do?
It provides instructions for scaffolding agent UIs with shadcn components. The assistant learns exact file paths, tool card conventions, and Vercel AI SDK integration rules.
What dependencies does the project need?
The UI requires React 19, Tailwind v4, and the ai and @tabler/icons-react packages.
Source text
SKILL.md · MIT
---
name: agent-elements
description: |
Use whenever the user wants to build or modify a chat, agent, or tool-calling
UI in a React 19 + Tailwind v4 project — especially if the code imports from
`@/components/agent-elements/*` or the project has that folder on disk.
Triggers: "agent chat", "tool call UI", "streaming chat", "plan approval",
"AgentChat", "InputBar", "tool renderer", mentions of Agent Elements, or
requests to add a new agent surface with shadcn.
Do NOT use for plain chat UIs that don't need tool/plan/approval cards, or
for projects already committed to a different agent UI kit.
---
# Agent Elements skill
Project-aware context for building chat and agent UIs with **Agent Elements** —
an open-source shadcn registry at `https://agent-elements.21st.dev`.
## What this skill gives you
When this skill loads, you know:
1. **The registry is shadcn-compatible.** Every component is installed with
`npx shadcn@latest add https://agent-elements.21st.dev/r/<component>.json`.
Files land under `components/agent-elements/` (the library's internal
`components/` prefix is stripped — see Paths below).
2. **The API is typed around the Vercel AI SDK.** Messages are
`UIMessage[]` from `ai`, status is `ChatStatus`. `useChat()` plugs in
directly.
3. **The full component catalog with API shapes and composition rules** (see
sections below).
4. **Theming guardrails** — the Tailwind tokens Agent Elements depends on.
## Detection
Consider this project "Agent Elements-ready" if any of these are true:
- `components/agent-elements/` exists on disk
- `components.json` includes an alias or registry reference to Agent Elements
- `package.json` dependencies include `ai` + `@tabler/icons-react` and the user
mentions Agent Elements
If the folder does not exist yet, install on demand with:
```bash
npx shadcn@latest add https://agent-elements.21st.dev/r/agent-chat.json
```
`agent-chat` transitively pulls every other component it needs via
`registryDependencies` (MessageList, InputBar, tool renderers, shared utils).
## Paths (post-install layout)
After `shadcn add`, files sit under `@/components/agent-elements/` with this
shape:
```
components/agent-elements/
agent-chat.tsx
message-list.tsx
input-bar.tsx
markdown.tsx
user-message.tsx
error-message.tsx
text-shimmer.tsx
spiral-loader.tsx
input/
attachment-button.tsx
send-button.tsx
file-attachment.tsx
suggestions.tsx
model-picker.tsx
mode-selector.tsx
tools/
bash-tool.tsx
edit-tool.tsx
search-tool.tsx
todo-tool.tsx
plan-tool.tsx
tool-group.tsx
subagent-tool.tsx
mcp-tool.tsx
thinking-tool.tsx
generic-tool.tsx
question/
question-tool.tsx
hooks/use-tool-complete.ts
utils/cn.ts
types.ts
```
**Import rule:** always import from the exact file, never from a barrel.
```tsx
// ✅
import { AgentChat } from "@/components/agent-elements/agent-chat";
import { BashTool } from "@/components/agent-elements/tools/bash-tool";
// ❌ — no barrel exists
import { AgentChat } from "@/components/agent-elements";
```
## Component catalog
### Chat surface
- **AgentChat** — the full chat shell. Renders `MessageList` + `InputBar`,
handles tool invocations via `toolRenderers`, shows an empty state with
optional `suggestions`. Props: `messages`, `status`, `onSend`, `onStop`,
`toolRenderers?`, `suggestions?`, `attachments?`, `classNames?`, `slots?`.
- **MessageList** — transcript only. Use when you need the input bar somewhere
else. Accepts `toolRenderers` and `showCopyToolbar`.
- **UserMessage / ErrorMessage / Markdown** — low-level message pieces.
`Markdown` streams safely (external links get `rel="noreferrer"` by default).
### Input
- **InputBar** — composer. Props: `status`, `onSend({ content })`, `onStop`,
`value?` + `onChange?` (controlled), `attachedImages`/`attachedFiles` with
their remove handlers, `leftActions`/`rightActions` slots, `suggestions?`,
`questionBar?`, `infoBar?`.
- **Suggestions** — quick-prompt chips for the empty state or inline.
- **ModelPicker / ModeSelector** — designed to drop into `leftActions`. Both
accept a simple `{ id, name, version? }` / `{ id, label, icon?, description? }`
shape. Do not import `CLAUDE_MODELS` — it was removed; supply your own array.
- **SendButton / AttachmentButton / FileAttachment** — usable standalone if
you're building a custom composer.
### Tool cards
All tool cards accept a `part` prop of type
`Extract<UIMessage["parts"][number], { type: \`tool-<Name>\` }>` from the AI
SDK. Register them via `toolRenderers` on `AgentChat`/`MessageList`:
```tsx
<AgentChat
toolRenderers={{
Bash: BashTool,
Edit: EditTool,
Write: EditTool, // Write reuses EditTool
Search: SearchTool,
WebSearch: SearchTool,
TodoWrite: TodoTool,
PlanWrite: PlanTool,
Task: SubagentTool,
Thinking: ThinkingTool,
}}
/>
```
Cards available:
- **BashTool** — command + stdout, collapsible.
- **EditTool** — diff card. Supports `input.old_string`/`input.new_string` or
`output.structuredPatch`, plus an approval footer via `input.approval`.
- **SearchTool** — grouped search results. Pass `results` or use `output.results`.
- **TodoTool** — diffed todo list from `input.todos` vs `output.oldTodos`.
- **PlanTool** — plan title + summary with approve/reject footer.
- **ToolGroup** — collapses consecutive tool calls into one row.
- **SubagentTool** — sub-agent task with nested tools.
- **McpTool** — generic MCP tool output; use `parseMcpToolType` from
`@/components/agent-elements/tools/tool-registry` to get `mcpInfo`.
- **ThinkingTool** — collapsible reasoning row.
- **GenericTool** — fallback for unknown tools.
- **QuestionTool** — clarifying question with single/multi/text answer kinds.
### Streaming states
- **TextShimmer** — shimmering status label.
- **SpiralLoader** — Lottie spiral; use for multi-second loading states.
## Composition patterns
### Full chat with tool rendering (most common)
```tsx
"use client";
import { AgentChat } from "@/components/agent-elements/agent-chat";
import { BashTool } from "@/components/agent-elements/tools/bash-tool";
import { EditTool } from "@/components/agent-elements/tools/edit-tool";
import { SearchTool } from "@/components/agent-elements/tools/search-tool";
import { useChat } from "@ai-sdk/react";
export default function Chat() {
const { messages, status, sendMessage, stop } = useChat();
return (
<AgentChat
messages={messages}
status={status}
onSend={({ content }) => sendMessage({ text: content })}
onStop={stop}
toolRenderers={{
Bash: BashTool,
Edit: EditTool,
Write: EditTool,
Search: SearchTool,
}}
/>
);
}
```
### Composer with mode + model pickers
```tsx
import { InputBar } from "@/components/agent-elements/input-bar";
import { ModeSelector } from "@/components/agent-elements/input/mode-selector";
import { ModelPicker } from "@/components/agent-elements/input/model-picker";
import { IconBulb, IconCursor } from "@tabler/icons-react";
const modes = [
{ id: "agent", label: "Agent", icon: IconCursor },
{ id: "plan", label: "Plan", icon: IconBulb },
];
const models = [
{ id: "sonnet", name: "Sonnet", version: "4.6" },
{ id: "opus", name: "Opus", version: "4.7" },
];
<InputBar
status="ready"
onSend={handleSend}
onStop={handleStop}
leftActions={
<>
<ModeSelector modes={modes} defaultValue="agent" />
<ModelPicker models={models} defaultValue="sonnet" />
</>
}
/>
```
### Custom tool renderer
`toolRenderers` values are React components that receive `{ part, chatStatus }`.
Return whatever UI you want; reuse `GenericTool` as a fallback shell.
## Theming
Agent Elements reads these Tailwind CSS vars (shadcn-style). Do not remove or
rename them in the consumer theme:
- `--an-foreground`, `--an-background`, `--an-primary-color`
- Standard shadcn tokens: `--background`, `--foreground`, `--border`,
`--muted`, `--muted-foreground`, `--accent`, `--primary`, etc.
Customising a component is just editing the installed file. Prefer that over
wrapping — the code is yours now.
## When NOT to use Agent Elements
- Projects using `assistant-ui`, `ai-elements`, `copilotkit`, or another kit —
don't mix.
- Pure chat UIs that never render tool calls or plans — `InputBar` + your own
message rendering may be enough; skip `AgentChat`.
- React < 19 or Tailwind < v4 — the components depend on both.
## Quick answers for common asks
- **"Add Agent Elements to this project"** → run `npx shadcn@latest init` if
`components.json` is missing, then
`npx shadcn@latest add https://agent-elements.21st.dev/r/agent-chat.json`.
- **"Switch the default SendButton look"** → edit
`components/agent-elements/input/send-button.tsx` directly. Tokens live on
`--an-*` CSS vars.
- **"Render a custom tool"** → map its type in `toolRenderers`; fall back to
`GenericTool` for unknown tools.
- **"Use with useChat"** → pass `messages` and `status` straight through,
translate `sendMessage`/`stop` to `onSend({ content })`/`onStop`.
## Registry reference
- Index: `https://agent-elements.21st.dev/r/index.json`
- Per-component: `https://agent-elements.21st.dev/r/<id>.json`
- Full docs in one file:
`https://agent-elements.21st.dev/llms-full.txt`
© 21st-dev · MIT · 21st-dev/agent-elements