Get started
Installation
Set up Desyne in a Next.js, Vite or existing shadcn project, in a monorepo, or by copying files by hand.
Desyne installs through the shadcn CLI. The CLI reads the registry, copies component source into your project, installs npm dependencies and adds any CSS variables a component needs.
Requirements
React 19, Tailwind CSS v4 and a @/* import alias. Components import
@/lib/utils, @/lib/primitive and @/components/ui/*, the same paths a
shadcn project already uses.
Pick your setup
Create the app
npx create-next-app@latest my-app --typescript --tailwind --app
cd my-appInitialise shadcn
npx shadcn@latest initThis writes components.json, lib/utils.ts and the base tokens in app/globals.css.
Register the namespace
Add the Desyne registry to components.json:
{
"registries": {
"@desyne": "https://desyne.dev/r/{name}.json"
}
}Add a component
npx shadcn@latest add @desyne/buttonimport { Button } from "@/components/ui/button";
export default function Page() {
return <Button onPress={() => alert("Hello")}>Click me</Button>;
}Components that use hooks or React Aria are marked "use client", so they work in
App Router pages without extra wrappers. See Server components.
Monorepos
In a Turborepo or pnpm/Bun workspace, keep components in a shared package and point each app at it.
-
Run
npx shadcn@latest initin the shared UI package and in each app, so every workspace has its owncomponents.jsonwith the@desyneregistry entry. -
Install into the shared package with
--cwd(-c):Terminal npx shadcn@latest add @desyne/dialog -c packages/ui -
In each app, map the aliases the components import to the shared package:
apps/web/tsconfig.jsonjson { "compilerOptions": { "paths": { "@/components/ui/*": ["../../packages/ui/src/components/ui/*"], "@/lib/utils": ["../../packages/ui/src/lib/utils"], "@/lib/primitive": ["../../packages/ui/src/lib/primitive"], "@/*": ["./*"] } } } -
Tell Tailwind to scan the package and, in Next.js, transpile it:
apps/web/app/globals.csscss @import "tailwindcss"; @source "../../../packages/ui/src";apps/web/next.config.mjsjs export default { transpilePackages: ["@acme/ui"] };
This is exactly how this documentation site consumes the library.
Manual installation
You can skip the CLI and copy files yourself. Every component page has a Manual tab with its source, its npm dependencies and the other components it imports.
Install the shared dependencies
npm install react-aria-components tailwind-variants clsx tailwind-merge lucide-react tw-animate-cssSome components need more (sonner for Toast, recharts for Chart,
embla-carousel-react for Carousel, input-otp for Input OTP). Their pages list them.
Add the helpers
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}Then copy lib/primitive.ts from the registry (the files[0].content
field) or from any component's Manual tab.
Add the tokens
Your global CSS needs Tailwind, the animation utilities and the tokens:
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
:root {
--radius: 0.625rem;
--background: oklch(0.984 0.002 286);
--foreground: oklch(0.205 0.006 286);
/* …the rest of the tokens */
}The full set, with dark values and the @theme inline mapping, is on
Colors & tokens.
Copy components
Copy each component into components/ui/ and fix the imports if your aliases differ.
Check it works
"use client";
import { Form } from "react-aria-components";
import { Button } from "@/components/ui/button";
import { TextField } from "@/components/ui/text-field";
export function Smoke() {
return (
<Form className="flex max-w-xs flex-col gap-4">
<TextField label="Email" name="email" type="email" isRequired />
<Button type="submit">Subscribe</Button>
</Form>
);
}Tab to the field: you should see the soft colored focus ring. Submit it empty: focus moves to the field and the required-field message appears under it.