Skip to content

Copy source

Every component is also published as source you copy into your project with your framework's registry CLI. The files become yours: you edit them freely, and nothing depends on the npm package at runtime.

Package or copied source

  • npm package (fancy-ui-svelte, fancy-ui-react, fancy-ui-vue): one dependency, one stylesheet import. Updates arrive by bumping the version. The package also ships the skin engine and the retro kit.
  • Copied source: the files of the components you pick land in your repository. You own them: edit, delete, refactor. Updates are a re-run of the add command and a diff you review.

Both give the same components with the same props. Pick per project; see the mixing rule below before combining them.

Prerequisites

  • Tailwind CSS v4 and TypeScript.
  • A project initialised with the registry CLI of your framework. init writes components.json (with the ui alias the files land under), a cn helper and the semantic color tokens the components read.

Svelte

bash
npx shadcn-svelte@latest init

SvelteKit with the $lib alias. The components import cn from your own $lib/utils; nothing else from your project.

React

bash
npx shadcn@latest init

Vite or Next.js with the @/ path alias. Relative imports between the copied files have no extension, so both bundlers resolve them.

Vue

bash
npx shadcn-vue@latest init

Vite or Nuxt with the @/ path alias. Keep the default ui alias, @/components/ui: the Vue CLI places the files by their path, and with a custom alias such as @/shared/ui it nests them under <alias>/ui/fancy/. In Nuxt, keep the copied tree out of component auto-import:

ts
components: { dirs: [{ path: "~/components", ignore: ["ui/fancy/**"] }] }

Add a component

Each component page has the exact command in its install block, under Copy source. The pattern is the item's URL in the registry of your framework:

Svelte

bash
npx shadcn-svelte@latest add https://fancy-ui.rama.app/r/svelte/rainbow-button.json

Files land in fancy/<slug>/ under your ui alias. Import from there:

ts
import { RainbowButton } from "$lib/components/ui/fancy/rainbow-button";

React

bash
npx shadcn@latest add https://fancy-ui.rama.app/r/rainbow-button.json

Files land in fancy/components/<slug>/ under your ui alias. Import from there:

ts
import { RainbowButton } from "@/components/ui/fancy/components/rainbow-button";

Vue

bash
npx shadcn-vue@latest add https://fancy-ui.rama.app/r/vue/rainbow-button.json

Files land in fancy/components/<slug>/ under the default components/ui directory (a custom ui alias nests them under <alias>/ui/fancy/). Import from there:

ts
import { RainbowButton } from "@/components/ui/fancy/components/rainbow-button";

Shared files and npm dependencies

  • Shared modules (utils, internals, sound, …) are separate items. The CLI installs the ones a component needs, once, next to the components; a second component that needs them reuses the same files.
  • npm packages a component imports (for example canvas-confetti or three) are installed at the range the package itself is built against, with their @types packages when TypeScript needs them.
  • Copied files never import fancy-ui-svelte, fancy-ui-react, fancy-ui-vue or fancy-ui-cameleon; the registry build fails if one would.

Updating a copied component

Every item carries meta.version, the version of the npm package its files were cut from, and meta.license. Open the item URL to see what the registry serves now. To update:

  • Commit your work, so the update is one reviewable diff.
  • Re-run the add command with --overwrite (-o for Vue). It rewrites the component and its shared files with the registry version.
  • Review the diff and keep the edits you still want.

Without --overwrite, the CLIs ask before replacing a file that already exists, and answering no keeps your copy. The Svelte CLI also asks when a component's shared items are already installed; run without a terminal (in a script or CI), it then installs nothing, so pass --overwrite there. The CLIs may reformat what they write: the React CLI can drop a file's leading comment, the Vue CLI re-indents.

Mixing the package and copies

One app can use the package for some components and copies for others, but never the same component from both. Each copy has its own internals (portal root, focus and dismissal helpers, ids, the sound engine and its settings), so a copied component does not share state with the package's, and two copies of a provider do not see each other. Pick one source per component, and import copied files only from your own tree.

Dark mode and tokens

The package's stylesheet declares the semantic tokens and a dark variant that follows a .dark class or, without one, the system preference. A copied install uses the tokens and the class-based dark variant your CLI init wrote instead, so dark mode follows .dark only. The components' light-dark() colors follow color-scheme; declare it if your CSS does not:

css
:root {
	color-scheme: light dark;
}
.dark {
	color-scheme: dark;
}

For dark: utilities that follow the system preference when no .dark or .light class is set, as the package does, declare its variant after @import "tailwindcss"; (your token block still switches on .dark):

css
@custom-variant dark {
	&:where(.dark, .dark *) {
		@slot;
	}
	@media (prefers-color-scheme: dark) {
		&:where(:not(.light, .light *)) {
			@slot;
		}
	}
}

What is not in the registries

The registries ship component code only. These stay out, per framework (paths relative to each package's source tree):

Svelte

  • root files: package entry barrel, component metadata, the token stylesheet
  • _template/: scaffolding template for new components
  • src/lib/cameleon/ (outside the source tree): skin engine and retro kit, npm only
  • _template/: scaffolding template for new components
  • other file kinds: only .svelte and .ts ship
  • unit tests: package test suite
  • test harnesses: test-only wrappers
  • web-audio-mock.ts: test double for the sound engine
  • READMEs: package documentation, not component code

React

  • root files but utils.ts: package entry barrel, test setup, SSR fixtures
  • cameleon/: skin engine and retro kit, npm only
  • other file kinds: only .ts, .tsx and .css ship
  • unit tests: package test suite
  • test harnesses: test-only wrappers
  • web-audio-mock.ts: test double for the sound engine
  • READMEs: package documentation, not component code

Vue

  • root files but utils.ts: package entry barrel, test setup, SSR fixtures
  • cameleon/: skin engine and retro kit, npm only
  • other file kinds: only .vue, .ts and .css ship
  • unit tests: package test suite
  • test fixtures: test-only data
  • test-setup.ts: test runner setup
  • web-audio-mock.ts: test double for the sound engine
  • READMEs: package documentation, not component code

Registry indexes

Each registry lists every item it serves. The files follow the JSON Schemas the CLIs publish, and every item names its author and MIT licence.