Skip to content

Commit c1e2982

Browse files
committed
feat: add apply command
1 parent 62f6df7 commit c1e2982

17 files changed

Lines changed: 1009 additions & 116 deletions

File tree

.changeset/common-pears-accept.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"shadcn": minor
3+
---
4+
5+
add shadcn apply command

apps/v4/content/docs/(root)/cli.mdx

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,44 @@ description: Use the shadcn CLI to add components to your project.
55

66
## init
77

8-
Use the `init` command to initialize configuration and dependencies for a new project.
8+
Use the `init` command to initialize configuration and dependencies for an existing project, or create a new project with `--name`.
99

1010
The `init` command installs dependencies, adds the `cn` util and configures CSS variables for the project.
1111

12+
For preset reapplication in an existing project, use [`apply`](#apply) when you want to overwrite preset-driven files and reinstall detected UI components. Use `init --force --no-reinstall` when you want to update config and CSS without reinstalling existing components.
13+
1214
```bash
1315
npx shadcn@latest init
1416
```
1517

1618
**Options**
1719

1820
```bash
19-
Usage: shadcn init [options] [components...]
21+
Usage: shadcn init|create [options] [components...]
2022

2123
initialize your project and install dependencies
2224

2325
Arguments:
24-
components name, url or local path to component
26+
components names, url or local path to component
2527

2628
Options:
2729
-t, --template <template> the template to use. (next, vite, start, react-router, laravel, astro)
2830
-b, --base <base> the component library to use. (radix, base)
29-
-p, --preset [name] use a preset configuration. (name, URL, or preset code)
30-
-n, --name <name> the name for the new project.
31-
-d, --defaults use default configuration. (default: false)
31+
--monorepo scaffold a monorepo project.
32+
--no-monorepo skip the monorepo prompt.
33+
-p, --preset [name] use a preset configuration
3234
-y, --yes skip confirmation prompt. (default: true)
35+
-d, --defaults use default configuration: --template=next --preset=base-nova (default: false)
3336
-f, --force force overwrite of existing configuration. (default: false)
3437
-c, --cwd <cwd> the working directory. defaults to the current directory.
38+
-n, --name <name> the name for the new project.
3539
-s, --silent mute output. (default: false)
36-
--monorepo scaffold a monorepo project.
37-
--no-monorepo skip the monorepo prompt.
38-
--reinstall re-install existing UI components.
39-
--no-reinstall do not re-install existing UI components.
40-
--rtl enable RTL support.
41-
--no-rtl disable RTL support.
4240
--css-variables use css variables for theming. (default: true)
4341
--no-css-variables do not use css variables for theming.
42+
--rtl enable RTL support.
43+
--no-rtl disable RTL support.
44+
--reinstall re-install existing UI components.
45+
--no-reinstall do not re-install existing UI components.
4446
-h, --help display help for command
4547
```
4648

@@ -52,6 +54,54 @@ npx shadcn@latest create
5254

5355
---
5456

57+
## apply
58+
59+
Use the `apply` command to apply a preset to an existing project.
60+
61+
`apply` overwrites preset-driven config, fonts, CSS variables, and detected installed UI components. Commit or stash your changes before continuing so you can easily go back.
62+
63+
`apply` requires an existing `components.json`. If your project is not initialized yet, run `shadcn init --preset <preset>` first.
64+
65+
```bash
66+
npx shadcn@latest apply --preset a2r6bw
67+
```
68+
69+
You can also pass the preset as a positional argument:
70+
71+
```bash
72+
npx shadcn@latest apply a2r6bw
73+
```
74+
75+
Named presets work the same way:
76+
77+
```bash
78+
npx shadcn@latest apply lyra
79+
```
80+
81+
If no preset is provided, the CLI offers to open the custom preset builder on `ui.shadcn.com/create`.
82+
83+
Use `apply` for overwrite/reinstall flows. If you want to refresh config and CSS without reinstalling existing components, use `init --force --no-reinstall` instead.
84+
85+
**Options**
86+
87+
```bash
88+
Usage: shadcn apply [options] [preset]
89+
90+
apply a preset to an existing project
91+
92+
Arguments:
93+
preset the preset to apply
94+
95+
Options:
96+
--preset <preset> preset configuration to apply
97+
-y, --yes skip confirmation prompt. (default: false)
98+
-c, --cwd <cwd> the working directory. defaults to the current directory.
99+
--silent mute output. (default: false)
100+
-h, --help display help for command
101+
```
102+
103+
---
104+
55105
## add
56106

57107
Use the `add` command to add components and dependencies to your project.

packages/shadcn/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ The `init` command installs dependencies, adds the `cn` util, configures Tailwin
2020
npx shadcn init
2121
```
2222

23+
## apply
24+
25+
Use the `apply` command to apply a preset to an existing project.
26+
27+
The `apply` command overwrites the current preset configuration, reinstalls detected UI components, and updates fonts and CSS variables to match the new preset.
28+
29+
```bash
30+
npx shadcn apply --preset a2r6bw
31+
```
32+
2333
## add
2434

2535
Use the `add` command to add components to your project.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { REGISTRY_URL } from "@/src/registry/constants"
2+
import { describe, expect, it } from "vitest"
3+
4+
import { resolveApplyInitUrl } from "./apply"
5+
6+
const SHADCN_URL = REGISTRY_URL.replace(/\/r\/?$/, "")
7+
8+
describe("resolveApplyInitUrl", () => {
9+
it("should include the inferred template for preset codes", () => {
10+
const { initUrl, presetBase } = resolveApplyInitUrl("a0", "base", {
11+
template: "next",
12+
rtl: true,
13+
})
14+
const parsed = new URL(initUrl)
15+
16+
expect(parsed.origin + parsed.pathname).toBe(`${SHADCN_URL}/init`)
17+
expect(parsed.searchParams.get("template")).toBe("next")
18+
expect(parsed.searchParams.get("preset")).toBe("a0")
19+
expect(parsed.searchParams.get("rtl")).toBe("true")
20+
expect(presetBase).toBeUndefined()
21+
})
22+
23+
it("should include the inferred template for named presets", () => {
24+
const { initUrl, presetBase } = resolveApplyInitUrl("lyra", "base", {
25+
template: "next",
26+
rtl: true,
27+
})
28+
const parsed = new URL(initUrl)
29+
30+
expect(parsed.origin + parsed.pathname).toBe(`${SHADCN_URL}/init`)
31+
expect(parsed.searchParams.get("template")).toBe("next")
32+
expect(parsed.searchParams.get("base")).toBe("base")
33+
expect(parsed.searchParams.get("rtl")).toBe("true")
34+
expect(presetBase).toBeUndefined()
35+
})
36+
37+
it("should preserve raw preset URLs without injecting a template", () => {
38+
const presetUrl = `${SHADCN_URL}/init?base=radix&style=nova&baseColor=neutral&theme=neutral&iconLibrary=lucide&font=inter&rtl=false&menuAccent=subtle&menuColor=default&radius=default`
39+
const { initUrl } = resolveApplyInitUrl(presetUrl, "base", {
40+
template: "next",
41+
})
42+
const parsed = new URL(initUrl)
43+
44+
expect(parsed.searchParams.get("template")).toBeNull()
45+
expect(parsed.searchParams.get("track")).toBe("1")
46+
})
47+
})

0 commit comments

Comments
 (0)