diff --git a/apps/v4/content/docs/registry/examples.mdx b/apps/v4/content/docs/registry/examples.mdx
index 66715513da..a537a4a505 100644
--- a/apps/v4/content/docs/registry/examples.mdx
+++ b/apps/v4/content/docs/registry/examples.mdx
@@ -44,7 +44,7 @@ The following registry item is a custom style that extends shadcn/ui. On `npx sh
The following registry item is a custom style that doesn't extend shadcn/ui. See the `extends: none` field.
-It can be used to create a new style from scratch i.e custom components, css vars, dependencies, etc.
+It can be used to create a new style from scratch, i.e. custom components, css vars, dependencies, etc.
On `npx shadcn add`, the following will:
@@ -69,21 +69,21 @@ On `npx shadcn add`, the following will:
],
"cssVars": {
"theme": {
- "font-sans": "Inter, sans-serif",
- }
+ "font-sans": "Inter, sans-serif"
+ },
"light": {
"main": "#88aaee",
"bg": "#dfe5f2",
"border": "#000",
"text": "#000",
- "ring": "#000",
+ "ring": "#000"
},
"dark": {
"main": "#88aaee",
"bg": "#272933",
"border": "#000",
"text": "#e6e6e6",
- "ring": "#fff",
+ "ring": "#fff"
}
}
}
@@ -147,7 +147,7 @@ The following style will init using shadcn/ui defaults and then add a custom `br
### Custom block
-This blocks installs the `login-01` block from the shadcn/ui registry.
+This block installs the `login-01` block from the shadcn/ui registry.
```json title="login-01.json" showLineNumbers
{
@@ -174,7 +174,7 @@ This blocks installs the `login-01` block from the shadcn/ui registry.
### Install a block and override primitives
-You can install a block fromt the shadcn/ui registry and override the primitives using your custom ones.
+You can install a block from the shadcn/ui registry and override the primitives using your custom ones.
On `npx shadcn add`, the following will:
@@ -877,7 +877,7 @@ Note: you need to define both `@keyframes` in css and `theme` in cssVars to use
You can add environment variables using the `envVars` field.
```json title="example-item.json" showLineNumbers {5-9}
-{»
+{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "custom-item",
"type": "registry:item",
@@ -921,7 +921,7 @@ Here's an example of a registry item that installs custom Cursor rules for _pyth
}
```
-Here's another example for installation custom ESLint config:
+Here's another example for installing a custom ESLint config:
```json title=".eslintrc.json" showLineNumbers {9}
{
@@ -944,7 +944,7 @@ You can also have a universal item that installs multiple files:
```json title="my-custom-starter-template.json" showLineNumbers {9}
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
- "name": "my-custom-start-template",
+ "name": "my-custom-starter-template",
"type": "registry:item",
"dependencies": ["better-auth"],
"files": [
diff --git a/apps/v4/content/docs/registry/faq.mdx b/apps/v4/content/docs/registry/faq.mdx
index 2e1963caae..a59cae6f02 100644
--- a/apps/v4/content/docs/registry/faq.mdx
+++ b/apps/v4/content/docs/registry/faq.mdx
@@ -36,7 +36,7 @@ Here's an example of a complex component that installs a page, two components, a
},
{
"path": "registry/new-york/hello-world/lib/format-date.ts",
- "type": "registry:utils"
+ "type": "registry:lib"
},
{
"path": "registry/new-york/hello-world/hello.config.ts",
diff --git a/apps/v4/content/docs/registry/open-in-v0.mdx b/apps/v4/content/docs/registry/open-in-v0.mdx
index 66ea29226a..c4416db364 100644
--- a/apps/v4/content/docs/registry/open-in-v0.mdx
+++ b/apps/v4/content/docs/registry/open-in-v0.mdx
@@ -18,7 +18,7 @@ See [Build your Open in v0 button](https://v0.dev/chat/button) for more informat
Here's a simple example of how to add a `Open in v0` button to your site.
-```jsx showLineNumbers
+```tsx showLineNumbers
import { Button } from "@/components/ui/button"
export function OpenInV0Button({ url }: { url: string }) {
diff --git a/apps/v4/content/docs/registry/registry-index.mdx b/apps/v4/content/docs/registry/registry-index.mdx
index aae64f7834..ad55d2a19a 100644
--- a/apps/v4/content/docs/registry/registry-index.mdx
+++ b/apps/v4/content/docs/registry/registry-index.mdx
@@ -56,7 +56,6 @@ Here's an example of a valid registry:
}
]
}
- }
]
}
```
diff --git a/apps/v4/package.json b/apps/v4/package.json
index 3f5a441df2..af10a7627b 100644
--- a/apps/v4/package.json
+++ b/apps/v4/package.json
@@ -76,7 +76,7 @@
"rehype-pretty-code": "^0.14.1",
"rimraf": "^6.0.1",
"server-only": "^0.0.1",
- "shadcn": "4.0.7",
+ "shadcn": "4.0.8",
"shiki": "^1.10.1",
"sonner": "^2.0.0",
"swr": "^2.3.6",
diff --git a/apps/v4/public/r/registries-legacy.json b/apps/v4/public/r/registries-legacy.json
index 995f92c5a3..1c25f0bd89 100644
--- a/apps/v4/public/r/registries-legacy.json
+++ b/apps/v4/public/r/registries-legacy.json
@@ -41,7 +41,7 @@
"@gaia": "https://ui.heygaia.io/r/{name}.json",
"@glass-ui": "https://glass-ui.crenspire.com/r/{name}.json",
"@heseui": "https://www.heseui.com/r/{name}.json",
- "@hooks": "https://shadcn-hooks.com/r/{name}.json",
+ "@shadcnhooks": "https://shadcn-hooks.com/r/{name}.json",
"@intentui": "https://intentui.com/r/{name}",
"@kibo-ui": "https://www.kibo-ui.com/r/{name}.json",
"@kanpeki": "https://kanpeki.vercel.app/r/{name}.json",
diff --git a/apps/v4/public/r/registries.json b/apps/v4/public/r/registries.json
index f2c0d2e4bc..652e11a585 100644
--- a/apps/v4/public/r/registries.json
+++ b/apps/v4/public/r/registries.json
@@ -11,6 +11,12 @@
"url": "https://ui.8starlabs.com/r/{name}.json",
"description": "A set of beautifully designed components designed for developers who want niche, high-utility UI elements that you won't find in standard libraries."
},
+ {
+ "name": "@unlumen-ui",
+ "homepage": "https://ui.unlumen.com",
+ "url": "https://ui.unlumen.com/r/{name}.json",
+ "description": "Primitives and components with serious attention to animation and design. Copy, own, ship."
+ },
{
"name": "@auth0",
"homepage": "https://auth0.com",
@@ -252,7 +258,7 @@
"description": "Ready-to-use foundation components/blocks built on top of shadcn/ui."
},
{
- "name": "@hooks",
+ "name": "@shadcnhooks",
"homepage": "https://shadcn-hooks.com",
"url": "https://shadcn-hooks.com/r/{name}.json",
"description": "A comprehensive React Hooks Collection built with Shadcn."
diff --git a/apps/v4/public/r/styles/new-york-v4/form-next-complex.json b/apps/v4/public/r/styles/new-york-v4/form-next-complex.json
index 9c4b29bb2d..750678a7cd 100644
--- a/apps/v4/public/r/styles/new-york-v4/form-next-complex.json
+++ b/apps/v4/public/r/styles/new-york-v4/form-next-complex.json
@@ -1,7 +1,6 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "form-next-complex",
- "type": "registry:example",
"registryDependencies": [
"field",
"input",
@@ -20,16 +19,7 @@
"path": "registry/new-york-v4/examples/form-next-complex.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport Form from \"next/form\"\nimport { toast } from \"sonner\"\n\nimport { Button } from \"@/registry/new-york-v4/ui/button\"\nimport { Card, CardContent, CardFooter } from \"@/registry/new-york-v4/ui/card\"\nimport { Checkbox } from \"@/registry/new-york-v4/ui/checkbox\"\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldLegend,\n FieldSeparator,\n FieldSet,\n FieldTitle,\n} from \"@/registry/new-york-v4/ui/field\"\nimport {\n RadioGroup,\n RadioGroupItem,\n} from \"@/registry/new-york-v4/ui/radio-group\"\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/registry/new-york-v4/ui/select\"\nimport { Spinner } from \"@/registry/new-york-v4/ui/spinner\"\nimport { Switch } from \"@/registry/new-york-v4/ui/switch\"\n\nimport { complexFormAction } from \"./form-next-complex-action\"\nimport { addons, type FormState } from \"./form-next-complex-schema\"\n\nexport default function FormNextComplex() {\n const [formState, formAction, pending] = React.useActionState<\n FormState,\n FormData\n >(complexFormAction, {\n values: {\n plan: \"basic\",\n billingPeriod: \"monthly\",\n addons: [],\n emailNotifications: false,\n },\n errors: null,\n success: false,\n })\n\n React.useEffect(() => {\n if (formState.success) {\n toast.success(\"Preferences saved\", {\n description: \"Your subscription plan has been updated.\",\n })\n }\n }, [formState.success])\n\n return (\n
\n \n \n \n \n \n \n \n \n \n )\n}\n",
"type": "registry:example"
- },
- {
- "path": "registry/new-york-v4/examples/form-next-complex-schema.ts",
- "content": "import { z } from \"zod\"\n\nexport const formSchema = z.object({\n plan: z\n .string({\n required_error: \"Please select a subscription plan\",\n })\n .min(1, \"Please select a subscription plan\")\n .refine((value) => value === \"basic\" || value === \"pro\", {\n message: \"Invalid plan selection. Please choose Basic or Pro\",\n }),\n billingPeriod: z\n .string({\n required_error: \"Please select a billing period\",\n })\n .min(1, \"Please select a billing period\"),\n addons: z\n .array(z.string())\n .min(1, \"Please select at least one add-on\")\n .max(3, \"You can select up to 3 add-ons\")\n .refine(\n (value) => value.every((addon) => addons.some((a) => a.id === addon)),\n {\n message: \"You selected an invalid add-on\",\n }\n ),\n emailNotifications: z.boolean(),\n})\n\nexport type FormState = {\n values: z.infer
\n errors: null | Partial, string[]>>\n success: boolean\n}\n\nexport const addons = [\n {\n id: \"analytics\",\n title: \"Analytics\",\n description: \"Advanced analytics and reporting\",\n },\n {\n id: \"backup\",\n title: \"Backup\",\n description: \"Automated daily backups\",\n },\n {\n id: \"support\",\n title: \"Priority Support\",\n description: \"24/7 premium customer support\",\n },\n] as const\n",
- "type": "registry:example"
- },
- {
- "path": "registry/new-york-v4/examples/form-next-complex-action.ts",
- "content": "\"use server\"\n\nimport { formSchema, type FormState } from \"./form-next-complex-schema\"\n\nexport async function complexFormAction(\n _prevState: FormState,\n formData: FormData\n) {\n // Sleep for 1 second\n await new Promise((resolve) => setTimeout(resolve, 1000))\n\n const values = {\n plan: formData.get(\"plan\") as FormState[\"values\"][\"plan\"],\n billingPeriod: formData.get(\"billingPeriod\") as string,\n addons: formData.getAll(\"addons\") as string[],\n emailNotifications: formData.get(\"emailNotifications\") === \"on\",\n }\n\n const result = formSchema.safeParse(values)\n\n if (!result.success) {\n return {\n values,\n success: false,\n errors: result.error.flatten().fieldErrors,\n }\n }\n\n // Do something with the values.\n // Call your database or API here.\n\n return {\n values,\n errors: null,\n success: true,\n }\n}\n",
- "type": "registry:example"
}
- ]
+ ],
+ "type": "registry:example"
}
\ No newline at end of file
diff --git a/apps/v4/public/r/styles/new-york-v4/form-next-demo.json b/apps/v4/public/r/styles/new-york-v4/form-next-demo.json
index b0d0325ad7..f5ac897d02 100644
--- a/apps/v4/public/r/styles/new-york-v4/form-next-demo.json
+++ b/apps/v4/public/r/styles/new-york-v4/form-next-demo.json
@@ -1,7 +1,6 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "form-next-demo",
- "type": "registry:example",
"registryDependencies": [
"field",
"input",
@@ -16,5 +15,6 @@
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport Form from \"next/form\"\nimport { toast } from \"sonner\"\n\nimport { Button } from \"@/registry/new-york-v4/ui/button\"\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/registry/new-york-v4/ui/card\"\nimport {\n Field,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n} from \"@/registry/new-york-v4/ui/field\"\nimport { Input } from \"@/registry/new-york-v4/ui/input\"\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupText,\n InputGroupTextarea,\n} from \"@/registry/new-york-v4/ui/input-group\"\nimport { Spinner } from \"@/registry/new-york-v4/ui/spinner\"\n\nimport { demoFormAction } from \"./form-next-demo-action\"\nimport { type FormState } from \"./form-next-demo-schema\"\n\nexport default function FormNextDemo() {\n const [formState, formAction, pending] = React.useActionState<\n FormState,\n FormData\n >(demoFormAction, {\n values: {\n title: \"\",\n description: \"\",\n },\n errors: null,\n success: false,\n })\n const [descriptionLength, setDescriptionLength] = React.useState(0)\n\n React.useEffect(() => {\n if (formState.success) {\n toast(\"Thank you for your feedback\", {\n description: \"We'll review your report and get back to you soon.\",\n })\n }\n }, [formState.success])\n\n React.useEffect(() => {\n setDescriptionLength(formState.values.description.length)\n }, [formState.values.description])\n\n return (\n \n \n Bug Report\n \n Help us improve by reporting bugs you encounter.\n \n \n \n \n \n \n \n \n \n \n \n )\n}\n",
"type": "registry:example"
}
- ]
+ ],
+ "type": "registry:example"
}
\ No newline at end of file
diff --git a/apps/v4/public/r/styles/new-york-v4/registry.json b/apps/v4/public/r/styles/new-york-v4/registry.json
index 2fb79c73ae..bcea5cd601 100644
--- a/apps/v4/public/r/styles/new-york-v4/registry.json
+++ b/apps/v4/public/r/styles/new-york-v4/registry.json
@@ -3696,6 +3696,47 @@
],
"type": "registry:example"
},
+ {
+ "name": "form-next-demo",
+ "registryDependencies": [
+ "field",
+ "input",
+ "textarea",
+ "button",
+ "card",
+ "spinner"
+ ],
+ "files": [
+ {
+ "path": "registry/new-york-v4/examples/form-next-demo.tsx",
+ "type": "registry:example"
+ }
+ ],
+ "type": "registry:example"
+ },
+ {
+ "name": "form-next-complex",
+ "registryDependencies": [
+ "field",
+ "input",
+ "textarea",
+ "button",
+ "card",
+ "spinner",
+ "checkbox",
+ "dialog",
+ "radio-group",
+ "select",
+ "switch"
+ ],
+ "files": [
+ {
+ "path": "registry/new-york-v4/examples/form-next-complex.tsx",
+ "type": "registry:example"
+ }
+ ],
+ "type": "registry:example"
+ },
{
"name": "form-rhf-demo",
"dependencies": ["react-hook-form", "@hookform/resolvers", "zod"],
diff --git a/apps/v4/registry/__index__.tsx b/apps/v4/registry/__index__.tsx
index f1f0f65357..2d4efb54ca 100644
--- a/apps/v4/registry/__index__.tsx
+++ b/apps/v4/registry/__index__.tsx
@@ -4888,6 +4888,44 @@ export const Index: Record> = {
categories: undefined,
meta: undefined,
},
+ "form-next-demo": {
+ name: "form-next-demo",
+ title: "undefined",
+ description: "",
+ type: "registry:example",
+ registryDependencies: ["field","input","textarea","button","card","spinner"],
+ files: [{
+ path: "registry/new-york-v4/examples/form-next-demo.tsx",
+ type: "registry:example",
+ target: ""
+ }],
+ component: React.lazy(async () => {
+ const mod = await import("@/registry/new-york-v4/examples/form-next-demo.tsx")
+ const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') || item.name
+ return { default: mod.default || mod[exportName] }
+ }),
+ categories: undefined,
+ meta: undefined,
+ },
+ "form-next-complex": {
+ name: "form-next-complex",
+ title: "undefined",
+ description: "",
+ type: "registry:example",
+ registryDependencies: ["field","input","textarea","button","card","spinner","checkbox","dialog","radio-group","select","switch"],
+ files: [{
+ path: "registry/new-york-v4/examples/form-next-complex.tsx",
+ type: "registry:example",
+ target: ""
+ }],
+ component: React.lazy(async () => {
+ const mod = await import("@/registry/new-york-v4/examples/form-next-complex.tsx")
+ const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') || item.name
+ return { default: mod.default || mod[exportName] }
+ }),
+ categories: undefined,
+ meta: undefined,
+ },
"form-rhf-demo": {
name: "form-rhf-demo",
title: "undefined",
diff --git a/apps/v4/registry/_legacy-colors.ts b/apps/v4/registry/_legacy-colors.ts
index d5da076ee5..1494a350b1 100644
--- a/apps/v4/registry/_legacy-colors.ts
+++ b/apps/v4/registry/_legacy-colors.ts
@@ -409,6 +409,322 @@ export const colors = {
oklch: "oklch(0.13,0.03,262)",
},
],
+ mauve: [
+ {
+ scale: 50,
+ hex: "#fafafb",
+ rgb: "rgb(250,250,251)",
+ hsl: "hsl(272.4,15%,98.2%)",
+ oklch: "oklch(0.98,0.00,310)",
+ },
+ {
+ scale: 100,
+ hex: "#f4f2f6",
+ rgb: "rgb(244,242,246)",
+ hsl: "hsl(272.4,16.1%,95.8%)",
+ oklch: "oklch(0.96,0.01,310)",
+ },
+ {
+ scale: 200,
+ hex: "#e5e1e8",
+ rgb: "rgb(229,225,232)",
+ hsl: "hsl(272.5,13%,89.6%)",
+ oklch: "oklch(0.92,0.01,310)",
+ },
+ {
+ scale: 300,
+ hex: "#d0c9d5",
+ rgb: "rgb(208,201,213)",
+ hsl: "hsl(272.6,12.6%,81.2%)",
+ oklch: "oklch(0.84,0.02,310)",
+ },
+ {
+ scale: 400,
+ hex: "#a69bae",
+ rgb: "rgb(166,155,174)",
+ hsl: "hsl(272.9,10.6%,64.6%)",
+ oklch: "oklch(0.70,0.03,310)",
+ },
+ {
+ scale: 500,
+ hex: "#81758b",
+ rgb: "rgb(129,117,139)",
+ hsl: "hsl(273.2,8.6%,50.1%)",
+ oklch: "oklch(0.58,0.04,310)",
+ },
+ {
+ scale: 600,
+ hex: "#675d70",
+ rgb: "rgb(103,93,112)",
+ hsl: "hsl(273.2,9.2%,40.2%)",
+ oklch: "oklch(0.49,0.03,310)",
+ },
+ {
+ scale: 700,
+ hex: "#524959",
+ rgb: "rgb(82,73,89)",
+ hsl: "hsl(273.3,9.7%,31.7%)",
+ oklch: "oklch(0.42,0.03,310)",
+ },
+ {
+ scale: 800,
+ hex: "#3d3642",
+ rgb: "rgb(61,54,66)",
+ hsl: "hsl(273.3,10.2%,23.6%)",
+ oklch: "oklch(0.34,0.02,310)",
+ },
+ {
+ scale: 900,
+ hex: "#2a252e",
+ rgb: "rgb(42,37,46)",
+ hsl: "hsl(273.3,10.9%,16.4%)",
+ oklch: "oklch(0.28,0.02,310)",
+ },
+ {
+ scale: 950,
+ hex: "#161218",
+ rgb: "rgb(22,18,24)",
+ hsl: "hsl(273.3,14.1%,8.3%)",
+ oklch: "oklch(0.19,0.01,310)",
+ },
+ ],
+ olive: [
+ {
+ scale: 50,
+ hex: "#f9faf9",
+ rgb: "rgb(249,250,249)",
+ hsl: "hsl(136.8,13.4%,97.9%)",
+ oklch: "oklch(0.98,0.00,155)",
+ },
+ {
+ scale: 100,
+ hex: "#f1f4f2",
+ rgb: "rgb(241,244,242)",
+ hsl: "hsl(136.9,14.2%,95.2%)",
+ oklch: "oklch(0.96,0.01,155)",
+ },
+ {
+ scale: 200,
+ hex: "#dee5e0",
+ rgb: "rgb(222,229,224)",
+ hsl: "hsl(137,10.6%,88.5%)",
+ oklch: "oklch(0.92,0.01,155)",
+ },
+ {
+ scale: 300,
+ hex: "#c4cfc8",
+ rgb: "rgb(196,207,200)",
+ hsl: "hsl(137.2,10.3%,79.2%)",
+ oklch: "oklch(0.84,0.02,155)",
+ },
+ {
+ scale: 400,
+ hex: "#94a599",
+ rgb: "rgb(148,165,153)",
+ hsl: "hsl(137.8,8.7%,61.4%)",
+ oklch: "oklch(0.70,0.03,155)",
+ },
+ {
+ scale: 500,
+ hex: "#6c8072",
+ rgb: "rgb(108,128,114)",
+ hsl: "hsl(138.4,8.6%,46.3%)",
+ oklch: "oklch(0.58,0.03,155)",
+ },
+ {
+ scale: 600,
+ hex: "#56675b",
+ rgb: "rgb(86,103,91)",
+ hsl: "hsl(138.4,9.1%,37%)",
+ oklch: "oklch(0.49,0.03,155)",
+ },
+ {
+ scale: 700,
+ hex: "#435147",
+ rgb: "rgb(67,81,71)",
+ hsl: "hsl(138.4,9.5%,29.1%)",
+ oklch: "oklch(0.42,0.02,155)",
+ },
+ {
+ scale: 800,
+ hex: "#313c35",
+ rgb: "rgb(49,60,53)",
+ hsl: "hsl(138.4,10.2%,21.5%)",
+ oklch: "oklch(0.34,0.02,155)",
+ },
+ {
+ scale: 900,
+ hex: "#222a24",
+ rgb: "rgb(34,42,36)",
+ hsl: "hsl(138.5,11.2%,14.8%)",
+ oklch: "oklch(0.28,0.02,155)",
+ },
+ {
+ scale: 950,
+ hex: "#101512",
+ rgb: "rgb(16,21,18)",
+ hsl: "hsl(138.4,14.2%,7.3%)",
+ oklch: "oklch(0.19,0.01,155)",
+ },
+ ],
+ mist: [
+ {
+ scale: 50,
+ hex: "#f9fafb",
+ rgb: "rgb(249,250,251)",
+ hsl: "hsl(189.3,20.3%,97.9%)",
+ oklch: "oklch(0.98,0.00,210)",
+ },
+ {
+ scale: 100,
+ hex: "#f0f4f5",
+ rgb: "rgb(240,244,245)",
+ hsl: "hsl(189.4,21.6%,95.1%)",
+ oklch: "oklch(0.96,0.01,210)",
+ },
+ {
+ scale: 200,
+ hex: "#dce5e6",
+ rgb: "rgb(220,229,230)",
+ hsl: "hsl(189.4,16.2%,88.4%)",
+ oklch: "oklch(0.92,0.01,210)",
+ },
+ {
+ scale: 300,
+ hex: "#c1cfd2",
+ rgb: "rgb(193,207,210)",
+ hsl: "hsl(189.4,15.8%,79%)",
+ oklch: "oklch(0.84,0.02,210)",
+ },
+ {
+ scale: 400,
+ hex: "#8da5a9",
+ rgb: "rgb(141,165,169)",
+ hsl: "hsl(189.3,14%,60.9%)",
+ oklch: "oklch(0.70,0.03,210)",
+ },
+ {
+ scale: 500,
+ hex: "#648085",
+ rgb: "rgb(100,128,133)",
+ hsl: "hsl(189.3,14.2%,45.7%)",
+ oklch: "oklch(0.58,0.03,210)",
+ },
+ {
+ scale: 600,
+ hex: "#4f676b",
+ rgb: "rgb(79,103,107)",
+ hsl: "hsl(189.3,15.1%,36.5%)",
+ oklch: "oklch(0.49,0.03,210)",
+ },
+ {
+ scale: 700,
+ hex: "#3d5155",
+ rgb: "rgb(61,81,85)",
+ hsl: "hsl(189.3,15.9%,28.6%)",
+ oklch: "oklch(0.42,0.03,210)",
+ },
+ {
+ scale: 800,
+ hex: "#2d3c3f",
+ rgb: "rgb(45,60,63)",
+ hsl: "hsl(189.3,17.2%,21.2%)",
+ oklch: "oklch(0.34,0.02,210)",
+ },
+ {
+ scale: 900,
+ hex: "#1e2a2c",
+ rgb: "rgb(30,42,44)",
+ hsl: "hsl(189.3,19.2%,14.6%)",
+ oklch: "oklch(0.28,0.02,210)",
+ },
+ {
+ scale: 950,
+ hex: "#0e1517",
+ rgb: "rgb(14,21,23)",
+ hsl: "hsl(189.3,25.3%,7.1%)",
+ oklch: "oklch(0.19,0.01,210)",
+ },
+ ],
+ taupe: [
+ {
+ scale: 50,
+ hex: "#fbfaf9",
+ rgb: "rgb(251,250,249)",
+ hsl: "hsl(34,21.3%,97.9%)",
+ oklch: "oklch(0.98,0.00,75)",
+ },
+ {
+ scale: 100,
+ hex: "#f5f3f0",
+ rgb: "rgb(245,243,240)",
+ hsl: "hsl(34,22.7%,95.1%)",
+ oklch: "oklch(0.96,0.01,75)",
+ },
+ {
+ scale: 200,
+ hex: "#e7e2dc",
+ rgb: "rgb(231,226,220)",
+ hsl: "hsl(34.1,18.7%,88.4%)",
+ oklch: "oklch(0.92,0.01,75)",
+ },
+ {
+ scale: 300,
+ hex: "#d3cbc0",
+ rgb: "rgb(211,203,192)",
+ hsl: "hsl(34.1,18.3%,79%)",
+ oklch: "oklch(0.84,0.02,75)",
+ },
+ {
+ scale: 400,
+ hex: "#aa9e8d",
+ rgb: "rgb(170,158,141)",
+ hsl: "hsl(34.2,14.8%,61.1%)",
+ oklch: "oklch(0.70,0.03,75)",
+ },
+ {
+ scale: 500,
+ hex: "#867865",
+ rgb: "rgb(134,120,101)",
+ hsl: "hsl(34.3,14.1%,46.1%)",
+ oklch: "oklch(0.58,0.03,75)",
+ },
+ {
+ scale: 600,
+ hex: "#6c6050",
+ rgb: "rgb(108,96,80)",
+ hsl: "hsl(34.3,14.9%,36.8%)",
+ oklch: "oklch(0.49,0.03,75)",
+ },
+ {
+ scale: 700,
+ hex: "#554b3e",
+ rgb: "rgb(85,75,62)",
+ hsl: "hsl(34.3,15.7%,28.9%)",
+ oklch: "oklch(0.42,0.03,75)",
+ },
+ {
+ scale: 800,
+ hex: "#40382d",
+ rgb: "rgb(64,56,45)",
+ hsl: "hsl(34.3,17%,21.4%)",
+ oklch: "oklch(0.34,0.02,75)",
+ },
+ {
+ scale: 900,
+ hex: "#2c271f",
+ rgb: "rgb(44,39,31)",
+ hsl: "hsl(34.3,17.7%,14.8%)",
+ oklch: "oklch(0.28,0.02,75)",
+ },
+ {
+ scale: 950,
+ hex: "#17130e",
+ rgb: "rgb(23,19,14)",
+ hsl: "hsl(34.4,24.8%,7.2%)",
+ oklch: "oklch(0.19,0.01,75)",
+ },
+ ],
red: [
{
scale: 50,
diff --git a/apps/v4/registry/directory.json b/apps/v4/registry/directory.json
index 3ca3c1c4a0..891ab13ab3 100644
--- a/apps/v4/registry/directory.json
+++ b/apps/v4/registry/directory.json
@@ -13,6 +13,13 @@
"description": "A set of beautifully designed components designed for developers who want niche, high-utility UI elements that you won't find in standard libraries.",
"logo": ""
},
+ {
+ "name": "@unlumen-ui",
+ "homepage": "https://ui.unlumen.com",
+ "url": "https://ui.unlumen.com/r/{name}.json",
+ "description": "Primitives and components with serious attention to animation and design. Copy, own, ship.",
+ "logo": ""
+ },
{
"name": "@auth0",
"homepage": "https://auth0.com",
@@ -295,7 +302,7 @@
"logo": ""
},
{
- "name": "@hooks",
+ "name": "@shadcnhooks",
"homepage": "https://shadcn-hooks.com",
"url": "https://shadcn-hooks.com/r/{name}.json",
"description": "A comprehensive React Hooks Collection built with Shadcn.",
diff --git a/apps/v4/registry/new-york-v4/examples/_registry.ts b/apps/v4/registry/new-york-v4/examples/_registry.ts
index 43ba976275..ac78c238fc 100644
--- a/apps/v4/registry/new-york-v4/examples/_registry.ts
+++ b/apps/v4/registry/new-york-v4/examples/_registry.ts
@@ -950,6 +950,47 @@ export const examples: Registry["items"] = [
},
],
},
+ {
+ name: "form-next-demo",
+ type: "registry:example",
+ registryDependencies: [
+ "field",
+ "input",
+ "textarea",
+ "button",
+ "card",
+ "spinner",
+ ],
+ files: [
+ {
+ path: "examples/form-next-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "form-next-complex",
+ type: "registry:example",
+ registryDependencies: [
+ "field",
+ "input",
+ "textarea",
+ "button",
+ "card",
+ "spinner",
+ "checkbox",
+ "dialog",
+ "radio-group",
+ "select",
+ "switch",
+ ],
+ files: [
+ {
+ path: "examples/form-next-complex.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
{
name: "form-rhf-demo",
type: "registry:example",
diff --git a/packages/shadcn/CHANGELOG.md b/packages/shadcn/CHANGELOG.md
index 741fafbbe8..8a9c7bdc3f 100644
--- a/packages/shadcn/CHANGELOG.md
+++ b/packages/shadcn/CHANGELOG.md
@@ -1,5 +1,11 @@
# @shadcn/ui
+## 4.0.8
+
+### Patch Changes
+
+- [#10041](https://github.com/shadcn-ui/ui/pull/10041) [`a97ebe54f1824032d8ad00d1d0c079e3dc6f52d7`](https://github.com/shadcn-ui/ui/commit/a97ebe54f1824032d8ad00d1d0c079e3dc6f52d7) Thanks [@shadcn](https://github.com/shadcn)! - Bundle @antfu/ni and tinyexec to fix missing module error with npx
+
## 4.0.7
### Patch Changes
diff --git a/packages/shadcn/package.json b/packages/shadcn/package.json
index 3d8390328b..52fe4fed2a 100644
--- a/packages/shadcn/package.json
+++ b/packages/shadcn/package.json
@@ -1,6 +1,6 @@
{
"name": "shadcn",
- "version": "4.0.7",
+ "version": "4.0.8",
"description": "Add components to your apps.",
"publishConfig": {
"access": "public"
@@ -79,7 +79,6 @@
"mcp:inspect": "pnpm dlx @modelcontextprotocol/inspector node dist/index.js mcp"
},
"dependencies": {
- "@antfu/ni": "^25.0.0",
"@babel/core": "^7.28.0",
"@babel/parser": "^7.28.0",
"@babel/plugin-transform-typescript": "^7.28.0",
@@ -116,6 +115,7 @@
"zod-to-json-schema": "^3.24.6"
},
"devDependencies": {
+ "@antfu/ni": "^25.0.0",
"@types/babel__core": "^7.20.5",
"@types/fs-extra": "^11.0.4",
"@types/prompts": "^2.4.9",
diff --git a/packages/shadcn/tsup.config.ts b/packages/shadcn/tsup.config.ts
index 12cb99bf2d..e7a411da60 100644
--- a/packages/shadcn/tsup.config.ts
+++ b/packages/shadcn/tsup.config.ts
@@ -19,6 +19,9 @@ export default defineConfig({
target: "esnext",
outDir: "dist",
treeshake: true,
+ // Bundle @antfu/ni and its dependency tinyexec to avoid
+ // module resolution failures with npx temporary installs.
+ noExternal: ["@antfu/ni", "tinyexec"],
onSuccess: async () => {
copyFileSync("src/tailwind.css", "dist/tailwind.css")
},
diff --git a/packages/tests/src/tests/init.test.ts b/packages/tests/src/tests/init.test.ts
index 358063700b..8433838949 100644
--- a/packages/tests/src/tests/init.test.ts
+++ b/packages/tests/src/tests/init.test.ts
@@ -12,9 +12,6 @@ import { createRegistryServer } from "../utils/registry"
describe("shadcn init - next-app", () => {
it("should init with default configuration", async () => {
- // Sleep for 1 second to avoid race condition with the registry server.
- await new Promise((resolve) => setTimeout(resolve, 2000))
-
const fixturePath = await createFixtureTestDirectory("next-app")
await npxShadcn(fixturePath, ["init", "--defaults"])
diff --git a/packages/tests/src/utils/helpers.ts b/packages/tests/src/utils/helpers.ts
index 31b0a10397..b6437e2dc5 100644
--- a/packages/tests/src/utils/helpers.ts
+++ b/packages/tests/src/utils/helpers.ts
@@ -47,7 +47,7 @@ export async function runCommand(
},
input: options?.input,
reject: false,
- timeout: options?.timeout ?? 30000,
+ timeout: options?.timeout ?? 60000,
})
const result = await childProcess
diff --git a/packages/tests/src/utils/setup.ts b/packages/tests/src/utils/setup.ts
index 98b3acf24c..6b14b85fb5 100644
--- a/packages/tests/src/utils/setup.ts
+++ b/packages/tests/src/utils/setup.ts
@@ -4,9 +4,59 @@ import { rimraf } from "rimraf"
export const TEMP_DIR = path.join(__dirname, "../../temp")
+const SHADCN_CLI_PATH = path.join(__dirname, "../../../shadcn/dist/index.js")
+
+async function waitForCondition(
+ label: string,
+ check: () => Promise,
+ timeoutMs = 60000
+) {
+ const deadline = Date.now() + timeoutMs
+ while (Date.now() < deadline) {
+ if (await check()) return
+ await new Promise((resolve) => setTimeout(resolve, 500))
+ }
+ throw new Error(`Timed out waiting for: ${label} (${timeoutMs}ms)`)
+}
+
export default async function setup() {
await fs.ensureDir(TEMP_DIR)
+ // The v4 dev script runs `pnpm --filter=shadcn build` in the background
+ // while `next dev` starts immediately. On fast CI runs the server can be
+ // ready before the CLI binary is built, so we wait for it explicitly.
+ await waitForCondition("shadcn CLI binary", () =>
+ fs.pathExists(SHADCN_CLI_PATH)
+ )
+
+ // The CLI's first request goes to the dynamic /init route. On a cold Next.js
+ // dev server, this route needs to be compiled on first access (~1.8s). That
+ // compilation time, on top of everything else init does, pushes the first
+ // test over the 30s CLI timeout. Warming up the route here ensures it is
+ // already compiled before any test starts.
+ const registryUrl = process.env.REGISTRY_URL || "http://localhost:4000/r"
+ const shadcnUrl = registryUrl.replace(/\/r\/?$/, "")
+ const initWarmupUrl = `${shadcnUrl}/init?base=base&style=nova&baseColor=neutral&theme=neutral&iconLibrary=lucide&font=geist&rtl=false&menuAccent=subtle&menuColor=default&radius=default&template=next`
+
+ await waitForCondition("init route warm-up", async () => {
+ try {
+ const res = await fetch(initWarmupUrl)
+ return res.ok
+ } catch {
+ return false
+ }
+ })
+
+ // The CLI fetches registry paths that may not exist (e.g. font files),
+ // causing Next.js to compile the /_not-found/page route on first 404.
+ // That compilation takes ~4-5s and contributes to the first test timing
+ // out. Trigger one 404 here so the not-found page is pre-compiled.
+ try {
+ await fetch(`${shadcnUrl}/r/styles/new-york-v4/font-geist.json`)
+ } catch {
+ // Best effort — don't block setup if this fails.
+ }
+
return async () => {
try {
await rimraf(TEMP_DIR)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4f89d549ec..01336aa661 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -278,7 +278,7 @@ importers:
specifier: ^0.0.1
version: 0.0.1
shadcn:
- specifier: 4.0.7
+ specifier: 4.0.8
version: link:../../packages/shadcn
shiki:
specifier: ^1.10.1
@@ -359,9 +359,6 @@ importers:
packages/shadcn:
dependencies:
- '@antfu/ni':
- specifier: ^25.0.0
- version: 25.0.0
'@babel/core':
specifier: ^7.28.0
version: 7.28.0
@@ -465,6 +462,9 @@ importers:
specifier: ^3.24.6
version: 3.24.6(zod@3.25.76)
devDependencies:
+ '@antfu/ni':
+ specifier: ^25.0.0
+ version: 25.0.0
'@types/babel__core':
specifier: ^7.20.5
version: 7.20.5