الطريقة الأولى: JSON static بـ build
npx shadcn@latest build- بيقرا
./registry.json(أو مسار تديهوله كـ argument). - بيكتب ملف لكل item في
./public/r، زيpublic/r/money-input.json، ومعاهمpublic/r/registry.json. - جوه ملف الـ item: محتوى كل ملف بيتحط في
content. وده الفرق بين الـ source والـ output. - لو فيه
include: بيتحل، والفهرس الناتج flat. - مكان تاني:
--output ./public/registryأو-o. - فولدر مختلف:
--cwdأو-c.
public/r/├── registry.json ← الفهرس، من غير content├── money-input.json ← فيه content كامل└── checkout-card.jsonبعدها أي static hosting بيقدّم الفولدر. في Next مثلًا npm run dev وبعدها الملفات على http://localhost:3000/r/money-input.json.
الطريقة التانية: route handlers من غير build
لو عايز الـ JSON يتبني وقت الطلب من الـ source، استخدم الـ loaders من shadcn/registry. ولازم shadcn يبقى runtime dependency:
npm install shadcnimport { loadRegistry } from "shadcn/registry"export async function GET() { try { return Response.json(await loadRegistry()) } catch (error) { console.error(error) return Response.json({ error: "الـ registry مش راضي يتحمل." }, { status: 500 }) }}import { loadRegistryItem, RegistryItemNotFoundError } from "shadcn/registry"export async function GET(_req: Request, ctx: { params: Promise<{ name: string }> }) { const { name } = await ctx.params try { return Response.json(await loadRegistryItem(name)) } catch (error) { if (error instanceof RegistryItemNotFoundError) { return Response.json({ error: `مفيش item اسمه "${name}".` }, { status: 404 }) } console.error(error) return Response.json({ error: "حصلت مشكلة." }, { status: 500 }) }}loadRegistry: بيرجّع الفهرس من غير محتوى الملفات، زيregistry.jsonالناتج من الـ build.loadRegistryItem: بيرجّع item واحد، ومعاه محتوى كل ملف متقري من الـ disk.- الاتنين بيحلّوا
includeلوحدهم، فمش محتاجbuildخالص.
Content negotiation: URL واحد لـ 3 جماهير
الـ CLI بيدعم HTTP content negotiation. يعني نفس الـ URL يقدر يرجّع:
- HTML للمتصفح: landing page أو docs.
- JSON للـ
shadcnCLI: item يتسطب. - Markdown للـ AI agents.
الـ CLI بيعرّف نفسه بالـ headers دي:
User-Agent: shadcnAccept: application/vnd.shadcn.v1+json, application/json;q=0.9import type { NextConfig } from "next"const cliHeader = { type: "header" as const, key: "accept", value: "(.*)application/vnd\\.shadcn\\.v1\\+json(.*)" }const cliAgent = { type: "header" as const, key: "user-agent", value: "shadcn" }const nextConfig: NextConfig = { async rewrites() { return { beforeFiles: [ { source: "/", has: [cliHeader], destination: "/r/index.json" }, { source: "/", has: [cliAgent], destination: "/r/index.json" }, ], } }, async headers() { // عشان أي CDN ميخزنش نسخة الـ HTML ويديها للـ CLI return [{ source: "/", headers: [{ key: "Vary", value: "Accept, User-Agent" }] }] },}export default nextConfigapp.get("/", (req, res) => { res.vary("Accept") res.vary("User-Agent") if (req.accepts("application/vnd.shadcn.v1+json")) return res.json(rootItem) if (req.get("User-Agent") === "shadcn") return res.json(rootItem) res.send(landingHtml)})- في Next الأحسن rewrite: القرار بيتاخد في الـ routing، فمش محتاج Proxy function.
- الـ item اللي بيتقدم على الـ root هنا مفترض إنه
/r/index.json، فغيّره حسب الـ output بتاعك. - النتيجة: المستخدم يكتب
shadcn add https://ui.nile.dev، فالـ URL أقصر، ومن غير/r/، وأسهل يتفتكر.
جرّب زي المستخدم بالظبط
القاعدة: الفهرس لـ list وsearch، وملف الـ item لـ view وadd.
npx shadcn@latest list http://localhost:3000/r/registry.jsonnpx shadcn@latest search http://localhost:3000/r/registry.json --query moneynpx shadcn@latest view http://localhost:3000/r/money-input.jsonnpx shadcn@latest add http://localhost:3000/r/money-input.json # من مشروع تانيnpx shadcn@latest registry add @nile=http://localhost:3000/r/{name}.jsonnpx shadcn@latest list @nilenpx shadcn@latest search @nile --query moneynpx shadcn@latest view @nile/money-inputnpx shadcn@latest add @nile/money-inputregistry addمحتاج مشروع فيهcomponents.json، وبيكتب الـ namespace جواه.{name}لازم يتحل لملف item. يعني@nile/money-inputيبقى.../r/money-input.json.- الفهرس بيتقدم لوحده على
.../r/registry.json.
Universal items — من غير framework
من إصدار 2.9.0 تقدر تعمل item يتسطب في أي فولدر، حتى لو مفيش components.json ومفيش framework.
- الشرط: كل ملف في الـ item يكون ليه
targetصريح. - الاستخدامات: Cursor rules أو ESLint config أو starter template فيه ملفات Vue.
{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "agent-rules", "type": "registry:item", "files": [ { "path": "rules/team.md", "type": "registry:file", "target": "~/AGENTS.md" }, { "path": "rules/python.mdc", "type": "registry:file", "target": "~/.cursor/rules/python.mdc" } ]}وأنا جربت item زي ده على فولدر فاضي: add كتب AGENTS.md على طول من غير ما يسأل عن components.json.
انشر
- ارفع المشروع على URL public.
- المستخدم يقدر يسطّب بالـ URL على طول، أو يضيف الـ namespace مرة واحدة:
npx shadcn@latest registry add @nile=https://nile.dev/r/{name}.jsonأو بإيده في components.json:
{ "registries": { "@nile": "https://nile.dev/r/{name}.json" }}ولو الـ registry open source، تقدر تسجّل الـ namespace في الـ Registry Directory، فالمستخدم يكتب @nile/... من غير ما يعرّف حاجة. وده في فصل ١٠.
🧪 Lab: registry من الصفر في 10 دقايق
mkdir nile-registry && cd nile-registryecho '{"name":"nile-registry","private":true}' > package.jsonmkdir -p components/ui rulesاكتب components/ui/money-input.tsx بأي component، وrules/team.md بأي كلام، وبعدين الفهرس:
{ "$schema": "https://ui.shadcn.com/schema/registry.json", "name": "nile", "homepage": "https://nile.dev", "items": [ { "name": "money-input", "type": "registry:ui", "description": "Input للمبالغ بالجنيه.", "files": [{ "path": "components/ui/money-input.tsx", "type": "registry:ui" }] }, { "name": "agent-rules", "type": "registry:item", "description": "قواعد الفريق للـ AI agents.", "files": [{ "path": "rules/team.md", "type": "registry:file", "target": "~/AGENTS.md" }] } ]}npx shadcn@latest registry validate # ✔ Registry is valid.npx shadcn@latest build # public/r/*.jsonpython3 -m http.server 3999 --directory public &npx shadcn@latest list http://localhost:3999/r/registry.json# - http://localhost:3999/r/money-input.json (ui) — Input للمبالغ بالجنيه.# - http://localhost:3999/r/agent-rules.json (item) — قواعد الفريق للـ AI agents.mkdir ../empty && cd ../emptynpx shadcn@latest add http://localhost:3999/r/agent-rules.json --yescat AGENTS.md # اتكتب في فولدر فاضي- افتح
public/r/money-input.jsonوشوف الـcontentاللي اتضاف. - جرّب
addللـmoney-inputفي الفولدر الفاضي، وشوف الفرق: ده مش universal، فمحتاج مشروع فيهcomponents.json.