Add with the CLI
Copy a component into your project and own the file.
Initialise once
init detects your framework and package manager, then copies the shared code and edits your CSS.
$ npx topclassui init
Detected Next.js 16.4.2, App Router, TypeScript 5.9.3, Tailwind 4.3.3
Detected npm (default)
CSS entry app/globals.css
Folder topclassui
svg → topclassui/components/svg/ (button imports it)
button → topclassui/components/button/ (toast imports it)
toast → topclassui/components/toast/ (base imports it)
base → topclassui/
Added to app/globals.css:
@import "../topclassui/tokens.css";
@import "../topclassui/styles.css";
Wrote 106 files and topclassui.jsonIt then installs @base-ui/react, class-variance-authority, clsx, tailwind-merge and zod, and prints the provider lines for your layout; it never edits the layout itself. If the layout already mounts the provider from the package, init stops, since the copies read the copied provider; --force goes ahead anyway.
Add a component
add copies the component and everything it imports. Name one or several, or pass --all.
$ npx topclassui add select
select → topclassui/components/select/
# the same command under each package manager
$ pnpm dlx topclassui add select
$ yarn dlx topclassui add select
$ bunx topclassui add selectlist prints every component and which ones are in your project; --json prints the same list as JSON.
What lands in your project
One folder for everything we add. Your own code stays untouched.
- your-app/
- app/
- globals.cssinit adds two import lines
- topclassui/everything of ours, in one folder
- components/
- button/init brings it, the toast imports it
- index.tsxthe component, renders on the server
- variants.tshow it looks
- classes.tsits own class constants
- constants.tsits default values
- types.tsits props
- schema.tsprop rules
- boundaries.tsthe limits it enforces
- skeleton.tsxloading
- dummy/working examples, one per file
- select/npx topclassui add select
- client.tsxthe interactive part only
- hook.tsstate, effects, handlers
- lib.tspure helpers, no React
- shared/cn, classes, contracts, labels, provider, theme
- icons/one file for each icon a copy uses
- tokens.cssevery --tc-* variable
- styles.cssthe @theme mapping and the variants
- topclassui.jsonyour settings
Import from the folder, not from the package: import { Button } from '@/topclassui/components/button'. The copies import each other with relative paths.
The config file
Written by init and read by add and diff. lock records each copied file with its version and a hash, so leave it to the CLI.
{
"version": 1,
"framework": "next-app",
"css": "app/globals.css",
"dir": "topclassui",
"imports": "relative",
"packageManager": "npm",
"lock": {
"registry": "0.1.0-beta.3",
"files": {
"components/button/index.tsx": { "from": "button", "version": "0.1.0-beta.3", "sha256": "…" }
}
}
}When you have edited a copied file
add never overwrites your edits. diff shows what changed; --overwrite replaces the file and prints the diff first.
$ npx topclassui diff select
select: 1 of 20 files differ
components/select/classes.ts: modified locally
$ npx topclassui add select --overwritediff exits with 1 when anything differs. After you update the package, add --update writes the new version of every file you have not touched.
Running without prompts
npx topclassui init --yes # accept every default
npx topclassui add button --yes
npx topclassui add button --dry-run # print what would be written, write nothingEvery command also takes --cwd <dir> for a project outside the current folder.