Installation

Set up Squad SDK in a Next.js or Vite project (both Tailwind v4) and install components from the registry.

Set up your project
Pick your framework. Every registry item is a framework-agnostic React component, so only the scaffold, the stylesheet location, and how you wire fonts + the squad-ui body class differ.

1. Scaffold + init. Create the app, generate the npm lockfile, then run shadcn init to write components.json.

$Terminal
# 1) Next.js (15+ recommended; the docs site runs on 16)
npx create-next-app@latest my-app
cd my-app

# 2) Generate the npm lockfile so shadcn's package-manager detector picks
#    npm (otherwise a stray ~/yarn.lock or ~/pnpm-lock.yaml further up the
#    tree can make it fail with "spawn yarn ENOENT" on the first add).
npm install

# 3) shadcn CLI — `init` writes `components.json` (path aliases for the
#    CLI). `add theme.json` will otherwise prompt for it on the first run
#    and the `--yes` flag does NOT auto-confirm that specific prompt.
npx shadcn@latest init --yes -d

2. Install the theme (tokens + cn helper + npm deps), then @import it from globals.css.

$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/theme.json --yes

# OPTIONAL — branded type (Albra headings + Inter body). Ships Albra as
# base64-embedded @font-face rules so there's nothing to download manually.
npx shadcn@latest add https://sdk-components.thesqd.com/r/typography.json --yes
CSSsrc/app/globals.css
/* src/app/globals.css */
@import "tailwindcss";               /* Tailwind v4 base */
@import "tw-animate-css";            /* enter/exit animations used by modals, popovers, etc. */
@import "shadcn/tailwind.css";       /* shadcn base utilities (cn, etc.) */
@plugin "@tailwindcss/typography";   /* prose styles required by RichTextEditor */
@import "../styles/squad-ui.css";    /* Squad tokens — dropped in by the theme install */

/* The squad-ui theme references --font-sans (body) and --font-heading.
   Wire them up in layout.tsx (next/font), or set fallbacks here if you
   want to ship without web fonts:

   .squad-ui {
     --font-sans: system-ui, -apple-system, "Segoe UI", sans-serif;
     --font-heading: var(--font-sans);
   }

   Squad components self-apply `squad-ui` to their roots — wrapping <body>
   in the class makes the tokens available to the rest of your app too. */

3. Wire fonts + the body class. Squad references --font-sans / --font-heading; wire them with next/font and put squad-ui on <body>.

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

// Squad UI components reference --font-sans (body) and --font-heading.
// Wire Inter to both so headings and body type render correctly out of
// the box. Override --font-heading in your overrides block if you want
// a different display font (e.g. Albra in the docs site).
const inter = Inter({ variable: "--font-sans", subsets: ["latin"] });

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className={inter.variable}>
      <body className="squad-ui">{children}</body>
    </html>
  );
}
2. Install components and blocks
Each registry item ships as one self-contained file. The CLI walks `registryDependencies` to install primitives a block depends on, and runs `npm install` for any npm packages declared in `dependencies`.
$Terminal
# Single component — primitives + npm deps auto-resolve
npx shadcn@latest add https://sdk-components.thesqd.com/r/badge.json

# Multiple at once
npx shadcn@latest add \
  https://sdk-components.thesqd.com/r/dialog.json \
  https://sdk-components.thesqd.com/r/alert-dialog.json

# A block — pulls every `registryDependency` (button, badge, tooltip, …)
# and runs `npm install` for declared `dependencies` (@dnd-kit/*, lucide-react, …)
npx shadcn@latest add https://sdk-components.thesqd.com/r/kanban-board-showcase.json
TSExample usage
import { Badge } from "@/components/ui/badge";
import { KanbanBoardShowcase } from "@/components/blocks/kanban-board-showcase";

export function Page() {
  return (
    <div>
      <Badge>Shipped</Badge>
      <KanbanBoardShowcase />
    </div>
  );
}
3. Override tokens (optional)
Squad ships sensible defaults for every token. To brand the components for your project, set your own values on `.squad-ui` after the import — source-order wins.
CSSsrc/app/globals.css
/* src/app/globals.css — add AFTER the squad-ui.css import.
   Source-order wins, so anything you set here replaces Squad defaults
   on every component, light + dark. */
.squad-ui {
  --primary: oklch(0.62 0.22 18);
  --radius: 0.5rem;
}

/* Per-mode overrides scope under the same dark selector Squad uses. */
.dark .squad-ui,
.squad-ui.dark {
  --primary: oklch(0.78 0.16 18);
}
Common pitfalls
Hands-on snags that bite when setting Squad UI up in a fresh project. Skim this before opening a bug report.
TXTShared (Next.js + Vite)
Most of these used to be manual setup steps — `theme.json` and individual
component manifests now resolve them automatically. Listed here so you know
what each install IS doing under the hood, and what's still on you.

✓ Auto-resolved by `shadcn add theme.json`:
- `src/lib/utils.ts` (the `cn` helper every component imports as `@/lib/utils`)
- npm: `clsx`, `tailwind-merge`, `tw-animate-css`, `@tailwindcss/typography`, `shadcn`
- `src/styles/squad-ui.css` (the tokens scoped under `.squad-ui`)
- `globals.css` patches: `@import` lines for `tw-animate-css`, `squad-ui.css`,
  the `@tailwindcss/typography` plugin, and a *conditional* system-font
  fallback under `.squad-ui` (self-referencing var fallback — only kicks
  in when `--font-sans` / `--font-heading` aren't set higher in the
  cascade, so it never fights `next/font`).

✓ Auto-resolved by individual component installs:
- All `registryDependencies` (e.g. installing `alert-dialog` pulls `alert`,
  `button`, `dialog`, etc. recursively).
- All npm `dependencies` declared on the manifest (e.g. `@dnd-kit/*` for
  the kanban block, `react-filepond` for file-upload-beta).
- Per-block extras: e.g. `file-upload-beta` ships `src/app/filepond.css`,
  `src/app/api/s3-presign/route.ts`, a `.env.example` template, AND an
  `@import "./filepond.css"` line auto-added to `globals.css` so the
  styling kicks in immediately.

⚠ Still manual after install (one line each):
1. `<body className="squad-ui">` in `layout.tsx`. shadcn's CLI doesn't
   modify JSX files, so this stays manual. The class is what makes
   tokens like `--background` / `--foreground` / `--radius` cascade to
   the rest of your app instead of just to Squad components.

2. Env vars for server routes. Blocks that ship a route handler also
   ship a `.env.example` with the matching variable names — copy it to
   `.env.local`, fill in the values, then restart `next dev`. Without
   them the route 500s on every request.

3. Deployed registry lag. `https://sdk-components.thesqd.com/r/<name>.json`
   is a CDN snapshot. New extras (CSS imports, routes) only show up
   after the docs redeploy. If you're testing a brand-new addition,
   point the CLI at the dev server URL or local registry JSON instead.

⚠ Environment gotchas (caught by a fresh-app smoke test):

4. `shadcn add` errors with `spawn yarn ENOENT`. shadcn's
   package-manager detector walks up the directory tree looking for the
   nearest lockfile. A stray `yarn.lock` in a parent directory (often
   `~/yarn.lock`) makes it pick yarn even when your project uses npm.
   Fix: run `npm install` in the project first so the app's own
   `package-lock.json` becomes the nearest lockfile — or delete the
   stray parent lockfile.

5. Next.js warns "multiple lockfiles" and infers your home dir as
   the workspace root. Same root cause as #4 — a stray
   `~/package-lock.json` outranks the app's. Either delete it or set
   `turbopack.root` in `next.config.ts`:

      import path from "node:path";
      export default { turbopack: { root: path.resolve(".") } };

6. `next dev` runs Turbopack even with `--no-turbopack`. Next 16
   defaults `next dev` to Turbopack regardless of the
   `create-next-app` flag (which only affects scaffolding). Pass
   `next dev --webpack` in your `package.json` dev script to opt
   out, or just embrace Turbopack — it's stable for this stack.

7. Branded fonts (Albra + Inter) need the `/typography` setup.
   `theme.json` ships only a self-referencing system-font fallback under
   `.squad-ui` — if you don't wire `next/font` with the `--font-sans`
   and `--font-heading` variables on `<html>`, you get system-ui (still
   readable, but not branded). Follow the `/typography` guide to load
   Inter via `next/font/google` and Albra via `next/font/local`. The
   theme's fallback is intentionally a no-op when those vars are set
   higher in the cascade, so you don't have to remove anything.
TXTVite only
Vite-specific snags on top of the shared list above:

1. `shadcn init` can't resolve "@/..." — the CLI reads your path alias
   from tsconfig. In a Vite scaffold the alias has to live in BOTH
   `tsconfig.json` and `tsconfig.app.json` (the app config is where the
   editor + build actually read `compilerOptions`). Add
   `"baseUrl": "."` and `"paths": { "@/*": ["./src/*"] }` to each.

2. Imports work in the editor but break at build — you set the tsconfig
   alias but forgot the matching `resolve.alias` in `vite.config.ts`.
   tsconfig paths drive type-checking; Vite needs its own alias to
   actually bundle them. See the "Project config" block above.

3. `@import "./styles/squad-ui.css"` 404s — the theme install writes the
   token file relative to your `components.json` css path. In a Vite app
   that's `src/index.css`, so the tokens land at `src/styles/squad-ui.css`
   and the import is `./styles/squad-ui.css` (not `../styles/...` like Next).

4. Tailwind utilities don't apply — make sure `@tailwindcss/vite` is in
   your `plugins` array in `vite.config.ts`. Vite uses the plugin, not a
   PostCSS config, for Tailwind v4.

5. No web font / wrong type — there's no `next/font`. Either load Inter
   via the `<link>` in `index.html` (shown above) or `@fontsource/inter`,
   then point `--font-sans` / `--font-heading` at it under `.squad-ui`.
   Skip it and you get the system-ui fallback (readable, not branded).
Monorepos (Turborepo / pnpm workspaces)
Squad UI ships with no `target` paths — the CLI writes each file wherever your `components.json` aliases `ui` to. Same install command for single-app and monorepo setups; only the alias differs.
Squad UI items declare `path: "src/components/ui/<name>.tsx"` in their
manifests with **no `target`** — the shadcn CLI strips the path to its
basename and writes the file wherever `aliases.ui` resolves in your
`components.json`. That means monorepo (Turborepo / pnpm workspaces) and
single-app projects use the same install command — only the `components.json`
in the directory you run `npx shadcn add` from differs.

Standard single-app setup:

```json
{
  "aliases": {
    "ui": "@/components/ui",
    "components": "@/components",
    "lib": "@/lib",
    "utils": "@/lib/utils",
    "hooks": "@/hooks"
  }
}
```
→ files land at `src/components/ui/<name>.tsx`.

Monorepo (Turborepo / pnpm) — put a `components.json` in `packages/ui/`:

```json
{
  "aliases": {
    "ui": "@workspace/ui/components",
    "components": "@workspace/ui/components",
    "lib": "@workspace/ui/lib",
    "utils": "@workspace/ui/lib/utils",
    "hooks": "@workspace/ui/hooks"
  }
}
```
→ files land at `packages/ui/src/components/<name>.tsx` and their internal
imports are auto-rewritten from `@/components/ui/<dep>` →
`@workspace/ui/components/<dep>`. Run `npx shadcn@latest add …` from inside
`packages/ui/` (or from any workspace whose own `components.json` aliases `ui`
to the same shared path).

⚠ Pitfall — DO NOT include a trailing `/ui` in your `ui` alias:

```json
"ui": "@workspace/ui/components/ui"   // ← results in components/ui/<name>.tsx, an extra dir level
"ui": "@workspace/ui/components"      // ← correct
```

The shadcn CLI appends `<basename>.tsx` directly to whatever the alias resolves
to. Trailing `/ui` creates a duplicated subdirectory — you'll see imports break
because Squad's registry items resolve their inter-dependencies as
`@<alias>/<dep>`, not `@<alias>/ui/<dep>`.
Browse the registry
Every published Squad UI item is indexed in a single JSON file at [`https://sdk-components.thesqd.com/r/registry.json`](https://sdk-components.thesqd.com/r/registry.json). Individual item manifests follow the pattern `https://sdk-components.thesqd.com/r/<name>.json` — point the shadcn CLI at any of them.
$Terminal
# Full registry index — names, types, files, registryDependencies for every item
curl https://sdk-components.thesqd.com/r/registry.json

# Pipe through jq for a quick item list
curl -s https://sdk-components.thesqd.com/r/registry.json | jq -r '.items[] | "\(.type)\t\(.name)"'
Manual install
If you can't run the shadcn CLI, every item's manifest JSON is fetchable directly. Paste the file content from the manifest into the path declared in its `files[].path` field.
Every registry item ships its full source inside its manifest JSON.
Open the URL in a browser to inspect the file paths, the
`registryDependencies` (other registry items the CLI will install),
and the `dependencies` (npm packages the CLI will `npm install`).

Manifest URL pattern:
https://sdk-components.thesqd.com/r/<name>.json

For example:
- https://sdk-components.thesqd.com/r/theme.json
- https://sdk-components.thesqd.com/r/badge.json
- https://sdk-components.thesqd.com/r/kanban-board-showcase.json

The full registry index (every published item, with names, types, and
dependencies) is available as a single JSON file:
https://sdk-components.thesqd.com/r/registry.json

A human-readable index also lives at /llms.txt.