Install
$ agentstack add skill-21st-dev-registry-21st-registry ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Publish & install components in the 21st team library
Use this skill in two directions: publishing a component for the team, and installing one the team already shared.
Pre-flight (always)
- Check auth:
API_KEY_21STenv is set, or the registry CLI has saved credentials. If neither — tell the user to runnpx @21st-dev/registry loginonce. Don't try to log them in yourself. - The CLI is
@21st-dev/registry. Don't reinvent — use it.
Publishing a component
Decide visibility — default to unlisted in a registry
| User says… | Visibility | |---|---| | "share with team", "залей нам", "publish internally", default for any unqualified ask | unlisted in the selected/default registry (no visibility flag needed) | | "share publicly via link / on my profile, but don't list it in the library" | --unlisted | | "publish publicly", "make it public on 21st" | --public | | "restrict to the registry team", "private team draft" | --private |
Never use --public without explicit user instruction. Public components go through admin moderation and appear in the 21st library. --unlisted is the safe option when the user wants a shareable URL but doesn't want a library listing.
Standard publish
The CLI's positional file path triggers auto-detection — name from the default export, slug from the filename, tags from imports, demo auto-found or synthesised. So in 95% of cases this is enough:
npx @21st-dev/registry ./path/to/Component.tsx \
--to default \
--description "1-2 sentences about what it does and when to use it"
Flag reference
| Flag | When to use | |---|---| | --name "Display Name" | Override the auto-detected name. Default is humanised version of the default-export name. | | --description "…" | Required. 10+ chars. Write a real description — what it does and when to use it. Never fabricate; if you don't know, ask the user. | | --tags "form,input,validation" | 1-5 lowercase tags. Default: detected from imports (lucide-react → "icon", framer-motion → "animation", etc). Only override if the auto-detected ones miss the point. | | --slug my-button | Override the URL slug. Default: kebab-case from name. | | --demo ./Component.demo.tsx | Demo file. Auto-detected by these patterns: {Component}.demo.tsx, demos/{slug}.tsx, demos/default.tsx. If none exist, the CLI synthesises a trivial ` demo automatically — fine for v1, but a real demo gives a much better preview. | | --preview ./preview.png | **Optional.** The team library uses a live iframe preview; a static image is only needed if you want a snappy thumbnail. | | --to | Target a specific registry in the authenticated team (e.g. --to default). If omitted, the server uses the team's first/default registry. | | --public / --unlisted / --private` | Override component visibility. |
What the user gets back
✅ https://21st.dev/community/components/{username}/{slug}
Install in another project:
npx @21st-dev/registry add @{username}/{slug}
Updating an existing component
Same command, same slug → upsert. The CLI prints "Updated" instead of "Published". No version flag needed; teammates always get the latest.
If the user says "I want a NEW component, not an update" but slug collides — confirm with the user before overwriting; suggest changing --slug.
Installing a component
Two address formats are accepted:
@team-slug/component-slug— install from a team registry (preferred for team-shared)@username/component-slug— install from a user's personal/public components
npx @21st-dev/registry add @acme/animated-button
# or
npx @21st-dev/registry add @serjobas/animated-button
The CLI:
- Fetches the registry JSON and component file (server resolves
@handleagainst team-slug first, then username) - Writes it to the project (
components/ui/{slug}.tsxby default) - Runs
pnpm/npm/yarn/bun addfor any npm dependencies - If it depends on other 21st components, prints them — install with
addseparately
For shadcn directly, public/unlisted components can use the plain registry item URL:
npx shadcn@latest add "https://21st.dev/r/acme/animated-button"
For repeat installs or private components, register the 21st registry in the consumer project's components.json and use a Bearer token:
{
"registries": {
"@YOUR_NAMESPACE": {
"url": "https://21st.dev/r/acme/default/{name}.json",
"headers": {
"Authorization": "Bearer ${API_KEY_21ST}"
}
}
}
}
Then install with the stock shadcn CLI:
npx shadcn@latest add @YOUR_NAMESPACE/animated-button
@YOUR_NAMESPACE is just the local alias from components.json; choose any clear slug. Do not commit real API keys into components.json unless the user explicitly wants that local file to contain a personal key.
Flags:
--force— overwrite existing file--no-install— skip npm install step (just write files)--dir PATH— install into a different project directory
Searching the team library
When the user wants a component but doesn't know the exact name:
npx @21st-dev/registry search ""
Default scope is team (your team's library). Use --scope mine for just your own, --scope public for the public library.
Always search before publishing if there's a chance a similar component already exists. Don't add duplicates to the team library.
npx @21st-dev/registry search "button" --scope team
# Prints list with @user/slug refs you can pass to `add`.
Managing teams & registries
View, create, and edit teams and registries from the CLI (no deletion — that stays in the web Studio). Every subcommand accepts --json; prefer it when you need to parse the output. Teams are addressed by slug with --team (default: the team the API key belongs to).
# Inspect before acting
npx @21st-dev/registry team list --json # all teams: slug, role, default
npx @21st-dev/registry team info [team] # members + pending invites
npx @21st-dev/registry registry list --team --json
# Create
npx @21st-dev/registry team create "" [--description TEXT]
npx @21st-dev/registry registry create "" [--slug SLUG] [--description TEXT] [--team SLUG]
# registry slug is auto-derived from the name if --slug is omitted
# Edit
npx @21st-dev/registry team edit [team] --name TEXT --description TEXT # owner only
npx @21st-dev/registry registry edit --name TEXT --description TEXT
# Invite a collaborator by email
npx @21st-dev/registry team invite [--team SLUG]
Typical flow before publishing a new component group: registry list --json to see what exists → registry create "Marketing blocks" if needed → publish --to marketing-blocks.
Hard rules for agents
- ❌ Never use
--publicwithout an explicit "publish publicly" from the user. - ❌ Never fabricate a description. Ask the user, or read the code carefully.
- ❌ Never include API keys, env values, or hardcoded internal URLs in the component file you publish.
- ❌ Never publish a file with unsaved edits — flush first.
- ✅ Always search before publishing if a similar component might already exist.
- ✅ Always add a useful demo file with realistic props if you can; only fall back to the auto-synthesised one as a last resort.
Common mistakes to avoid
- Demo imports the component via a wrong path. The CLI auto-rewrites relative imports (e.g.
import X from "../component") to@/components/ui/{slug}before upload — so write demos with relative imports to the user's source file, not aliases. The CLI will sort it out. - Slug doesn't match between publishes. If the user renames the file, the auto-derived slug changes and you'll create a duplicate. Pass
--slugexplicitly when re-publishing under a stable name. - Component lacks a default export. This will fail with a clear error — refactor the component to
export default function ComponentName(...)first.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: 21st-dev
- Source: 21st-dev/registry
- License: MIT
- Homepage: https://21st.dev
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.