21st-registry
GitHub用于在21st.dev团队库中发布或安装React组件。支持通过CLI自动检测元数据,处理身份验证,并根据用户指令设置可见性(公开、未列出或私有),便于团队成员共享和复用UI元素。
Trigger Scenarios
Install
npx skills add NeverSight/learn-skills.dev --skill 21st-registry -g -y
SKILL.md
Frontmatter
{
"name": "21st-registry",
"description": "Publish a React component to the user's team library on 21st.dev so teammates can install it with a single command, or install an existing one. Triggers when the user says things like \"publish\/share\/upload this to 21st\", \"залей в наш регистр\", \"опубликуй компонент\", \"share with team\", \"make this reusable\", \"install our Button\", \"use the team button\"."
}
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 <Component /> 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 <registry-slug> |
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 "<query>"
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`.
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.
Version History
- e0220ca Current 2026-07-05 21:06


