Copy components with shadcn

Lily publishes its React headless components as a shadcn registry, so npx shadcn add @lily/button copies a component, and any Lily component it imports, into your project as your own code.

When to copy, and when to install

Copying gives you the source to edit. Installing @lilydesignsystem/react-headless gives you updates. Pick one per component: a copy is a fork. Lily's own RFC 8 asks whether copying should be the primary path at all, because a copy does not receive fixes.

Step 1 — Add the Lily registry to components.json

The shadcn CLI (version 3 or later) reads namespaced registries from your project's components.json. Add Lily under the name @lily; the items refer to each other by that name.

{
  "$schema": "https://ui.shadcn.com/schema.json",
  "rsc": true,
  "tsx": true,
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils"
  },
  "registries": {
    "@lily": "https://lilydesignsystem.com/r/react/{name}.json"
  }
}

A project that already ran npx shadcn init only needs the registries entry. Lily's components use no Tailwind and no shadcn utilities; the CLI needs components.json only to know where your components directory is.

Step 2 — Add components

npx shadcn@latest add @lily/button
npx shadcn@latest add @lily/dialog @lily/scroller

Each item is the component's slug, as in the component list. Files land in one directory, components/lily/ (under src/ if your project has one), so the components' relative imports keep working: @lily/scroller also copies ScrollerBase.tsx, which it imports, and a component that uses a shared helper module (such as form-reset.ts) brings that module too. No npm package is installed; Lily's React components need only React.

import Button from "@/components/lily/Button";

export function SaveBar() {
  return <Button onClick={save}>Save</Button>;
}

Components that use React hooks start with "use client", so they work in the Next.js App Router; the rest stay server components. Each file keeps its header comment (props, keyboard, ARIA), moved below the directive or the imports, because the shadcn CLI drops comments that lead a file. Style the components with your own CSS against their class hooks, or with one of Lily's themes.

The registry

  • https://lilydesignsystem.com/r/react/registry.json lists every item (607 components and the shared helper modules) in shadcn's registry.json schema.
  • https://lilydesignsystem.com/r/react/{slug}.json is one registry item, with the file's content inlined. You can also add one by URL, with no components.json entry: npx shadcn add https://lilydesignsystem.com/r/react/button.json (an item with a dependency still needs the @lily entry).
  • https://lilydesignsystem.com/r/{slug}.json, without react/, is a different file: Lily's own per-component metadata (ARIA, keyboard, props, related components). It is not a shadcn item.

The registry is generated from the React headless source by bin/generate-registries, and CI runs npx shadcn add against it into a fresh Next.js app on every change.

What copying costs

A copied component is your code from the moment it lands. Lily cannot reach it again:

  • No updates. An accessibility fix to Lily's Dialog next year never reaches the Dialog you copied this year. Running npx shadcn add @lily/dialog again offers to overwrite your file with the new version, which discards your edits; there is no merge.
  • You own the tests. Lily's tests and stories stay in Lily. A copy is checked only by the tests you write for it.
  • Track what you copied. Watch Lily's CHANGELOG for the components you hold, and diff your copy against the current item when one changes.

If you do not need to edit a component, install the package instead and receive fixes with every upgrade. See the React tutorial.

Svelte and Vue

Only React is published as a shadcn registry so far. shadcn-svelte and shadcn-vue each define their own registry schema, and Lily has not yet tested its items with their CLIs. For Svelte and Vue, install the headless package or copy the component file from the repository.