Uqudo UIregistry

Installation

One command configures components.json, installs the Uqudo theme and registers the @uqudo namespace.

You only do this once per project. Afterwards every item is available as @uqudo/<name>.

Prerequisites

A Next.js project with Tailwind CSS v4 and a globals.css that starts with @import "tailwindcss";. Nothing else — the preset below writes components.json for you.

Set up

The init-uqudo-ui preset does three things in one go: it writes our components.json settings (Radix radix-rhea style, Lucide icons, RTL on), installs the theme as app/uqudo-theme.css and registers the @uqudo namespace.

Apply the preset

Pick the command that matches your project.

npx shadcn@latest init --preset "https://ui-components.dev.uqudo.io/r/init-uqudo-ui.json?base=radix" --yes

Keep ?base=radix and the quotes. Without the parameter the CLI assumes Base UI and the components will not match the ones documented here.

After it runs you have:

globals.css
uqudo-theme.css
ignore.mjs
init-uqudo-ui.json
theme.json
components.json

Clean up globals.css

The CLI added @import "./uqudo-theme.css"; to the top of globals.css, but it cannot delete what was already there. Remove the :root and .dark blocks — they sit after the import, so they would override the Uqudo tokens.

app/globals.css
@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";
@import "./uqudo-theme.css"; 

@custom-variant dark (&:is(.dark *));

@theme inline {
  /* keep — maps tokens to Tailwind utilities */
}

:root {
  --background: oklch(1 0 0);
  /* ... */
}

.dark {
  --background: oklch(0.145 0 0);
  /* ... */
}

@layer base {
  /* keep */
}

Keep @custom-variant dark, @theme inline and @layer base. If those blocks held project-specific variables, move them into a new :root after the import — anything declared there overrides the theme on purpose.

Load Inter

The theme's --font-sans reads a --font-inter variable. Provide it with next/font on the root element:

app/layout.tsx
import { Inter } from "next/font/google"

const inter = Inter({ subsets: ["latin"], variable: "--font-inter" })

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={inter.variable}>
      <body>{children}</body>
    </html>
  )
}

Ignore installed files

Each item writes .registry/<name>.json with the paths it owns. Hook ESLint and Prettier once so those files are skipped. After that, a new add is enough — do not edit ignore lists per item.

eslint.config.mjs
import { getRegistryIgnores } from "./.registry/ignore.mjs"

export default [
  { ignores: getRegistryIgnores() },
  // ...your existing config
]
package.json
{
  "scripts": {
    "format:check": "node .registry/ignore.mjs prettier --check .",
    "format:fix": "node .registry/ignore.mjs prettier --write ."
  }
}

The helper never rewrites eslint.config.mjs or .prettierignore.

Install an item

npx shadcn@latest add @uqudo/button

Dependencies resolve on their own — sidebar pulls in button, sheet, tooltip, the use-mobile hook and the registry ignore helper, and installs the cn and radix-ui npm packages it needs.

Where files land

UI primitives are written to components/uqudo/ui/ rather than components/ui/, so they sit alongside your own components without overwriting them. Blocks land one level up in components/uqudo/, hooks in hooks/.

button.tsx
dialog.tsx
welcome-card.tsx
use-mobile.ts
ignore.mjs
button.json
dialog.json

Swapping a project from its own shadcn copy to the registry is therefore a one-line change per import:

- import { Button } from "@/components/ui/button"
+ import { Button } from "@/components/uqudo/ui/button"

Without the preset

If you would rather not touch components.json settings, register the namespace and pull the theme in on its own. Set "style": "radix-rhea" and "iconLibrary": "lucide" by hand so installed components match.

npx shadcn@latest registry add "@uqudo=https://ui-components.dev.uqudo.io/r/{name}.json"
npx shadcn@latest add @uqudo/theme

Keep the quotes. Without them your shell will try to expand {name} and the registry URL ends up wrong.

A single item can be installed straight from its URL only if it has no registryDependencies. Every Uqudo item depends on @uqudo/registry-ignore, so register the namespace and install by name.

Updating an item

Re-run add. The CLI overwrites the files, so review the diff before committing — any local edits to those files are lost. Files that already exist, such as uqudo-theme.css, are only replaced with --overwrite.

npx shadcn@latest add @uqudo/button
npx shadcn@latest add @uqudo/theme --overwrite

Browsing the registry

The catalog is a plain JSON document you can open, script against, or point an MCP-enabled assistant at:

https://ui-components.dev.uqudo.io/r/registry.json

On this page