# Technical Documentation: Kiranism/next-shadcn-dashboard-starter

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/Kiranism/next-shadcn-dashboard-starter)

### Overview (`README.md`)

<h1 align="center">Admin Dashboard Template with Next.js &amp; Shadcn UI</h1>

<div align="center">Free, open source admin dashboard starter built with Next.js 16, shadcn/ui, Tailwind CSS, and TypeScript</div>

<div align="center">
  <a href="https://dub.sh/shadcn-dashboard"><strong>View Demo</strong></a>
</div>

<br />

<div align="center">
  <img src="/public/shadcn-dashboard.png" alt="Shadcn Dashboard Cover" style="max-width: 100%; border-radius: 8px;" />
</div>

<br />

<p align="center">
  <a href="https://github.com/Kiranism/next-shadcn-dashboard-starter/stargazers"><img src="https://img.shields.io/github/stars/Kiranism/next-shadcn-dashboard-starter?style=social" alt="GitHub stars" /></a>
  <a href="https://github.com/Kiranism/next-shadcn-dashboard-starter/network/members"><img src="https://img.shields.io/github/forks/Kiranism/next-shadcn-dashboard-starter?style=social" alt="Forks" /></a>
  <a href="https://github.com/Kiranism/next-shadcn-dashboard-starter/blob/main/LICENSE"><img src="https://img.shields.io/github/license/Kiranism/next-shadcn-dashboard-starter" alt="MIT License" /></a>
  <img src="https://img.shields.io/badge/Next.js-16-black" alt="Next.js" />
  <a href="https://go.clerk.com/ILdYhn7"><img src="https://img.shields.io/badge/Sponsored_by-Clerk-6C47FF?style=flat-square&logo=clerk" alt="Sponsored by Clerk" /></a>
</p>

## Overview

A free, open source (MIT) admin dashboard starter built with Next.js 16, shadcn/ui on Base UI primitives, TypeScript, and Tailwind CSS v4.

Every feature is a working, production-ready implementation, not static demo UI. Tables search, filter, sort, and paginate for real. Forms validate and mutate with cache invalidation.
Auth, organizations, and billing function end-to-end.

Clone it, strip what you don't need with the built-in cleanup script, and start building on patterns you'd write yourself. It works well as a base for SaaS apps, internal tools, and admin panels.

### Why This Template

Most dashboard templates are static demo boilerplates: screens that look finished but need rebuilding the moment you wire in real data. This starter takes the opposite approach:

- **Everything actually works.** Data tables run end-to-end: server prefetch, client-side React Query cache, and URL-synced search, filtering, sorting, and pagination via nuqs. Forms are built from reusable, composable fields with Zod validation, including advanced patterns like multi-step and dialog/sheet forms, with real create/update mutations and cache invalidation on success.
- **Industry-standard implementations.** The data layer follows the official TanStack Query SSR pattern (server prefetch + `HydrationBoundary` + `useSuspenseQuery`), typed end to end, organized in a feature-based structure with a clean API layer per feature. These are patterns you copy into production code as-is, not mockups you rebuild from scratch.
- **Minimal by design.** Deliberately lean, with no bloated boilerplate, so you spend your time tweaking it to your use case, not deleting someone else's code. The built-in [cleanup script](#cleanup-script-start-minimal-in-60-seconds) strips any feature you don't need in under a minute.

### Tech Stack

- Framework - [Next.js 16](https://nextjs.org/16)
- Language - [TypeScript](https://www.typescriptlang.org)
- Auth - [Clerk](https://go.clerk.com/ILdYhn7)
- Error tracking - [Sentry](https://sentry.io/for/nextjs/?utm_source=github&utm_medium=paid-community&utm_campaign=general-fy26q2-nextjs&utm_content=github-banner-project-tryfree)
- Styling - [Tailwind CSS v4](https://tailwindcss.com)
- Components - [shadcn/ui](https://ui.shadcn.com) on [Base UI](https://base-ui.com) primitives
- Charts - [Recharts](https://recharts.org) • [Evil Charts](https://evilcharts.com/)
- Schema validation - [Zod](https://zod.dev)
- Data fetching - [TanStack React Query](https://tanstack.com/query)
- State management - [Zustand](https://zustand-demo.pmnd.rs)
- Search param state - [Nuqs](https://nuqs.47ng.com/)
- Tables - [TanStack Data Tables](https://ui.shadcn.com/docs/components/data-table) • [Dice Table](https://diceui.com/docs/components/data-table)
- Forms - [TanStack Form](https://tanstack.com/form) + [Zod](https://zod.dev)
- Command+K interface - [kbar](https://kbar.vercel.app/)
- Linter / Formatter - [OxLint](https://oxc.rs/docs/guide/usage/linter) • [Oxfmt](https://oxc.rs/docs/guide/usage/formatter)
- Pre-commit hooks - [Husky](https://typicode.github.io/husky/)
- Themes - [tweakcn](https://tweakcn.com/)

_Looking for a TanStack Start version? Here's the [repo](https://git.new/tanstack-start-dashboard)._

## Features

- Pre-built dashboard layout with sidebar, header, and content area
- Analytics overview page with cards and charts
- Data tables with React Query prefetch, client-side cache, search, filter, and pagination
- Authentication and user management through Clerk
- Multi-tenant workspaces using Clerk Organizations (create, switch, manage teams)
- Billing and subscriptions via Clerk Billing for B2B, with plan management and feature gating
- Client-side RBAC navigation that filters menu items by organization, permissions, and roles
- Infobar component for tips, status messages, or contextual notes on any page
- shadcn/ui components on Base UI primitives, styled with Tailwind CSS
- Six-plus themes with a theme switcher
- Feature-based folder structure
- A starting point for SaaS dashboards, internal tools, and client admin panels

## Use Cases

A few things you can build with it:

- SaaS admin dashboards
- Internal tools and operations panels
- Analytics dashboards
- Client project admin panels
- A boilerplate for new Next.js shadcn projects

## Pages

| Page                                                                                                                                                                  | Notes                                                                                                                                                                                |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Signup / Signin](https://go.clerk.com/ILdYhn7)                                                                                                                       | Auth handled by Clerk, with passwordless sign-in, social logins, and enterprise SSO. |
| [Dashboard Overview](https://shadcn-dashboard.kiranism.dev/dashboard)                                                                                                 | Cards and Recharts graphs. Parallel routes give each section its own loading and error state.                                                                                       |
| [Product List (Table)](https://shadcn-dashboard.kiranism.dev/dashboard/product)                                                                                       | TanStack Table plus React Query (server prefetch, client cache) with nuqs URL state for search, filter, and pagination. `shallow: true` keeps interactions on the client.           |
| [Create Product Form](https://shadcn-dashboard.kiranism.dev/dashboard/product/new)                                                                                    | TanStack Form and Zod with `useMutation` for create and update. Cache is invalidated on success.                                                                                    |
| [Users (Table)](https://shadcn-dashboard.kiranism.dev/dashboard/users)                                                                                                | Same setup as Products: React Query with nuqs, server prefetch, and client-side pagination and filtering.                                                                           |
| [React Query Demo](https://shadcn-dashboard.kiranism.dev/dashboard/react-query)                                                                                       | A Pokemon API example showing the server prefetch, `HydrationBoundary`, and `useSuspenseQuery` pattern with client-side cache. |
| [Profile](https://shadcn-dashboard.kiranism.dev/dashboard/profile)                                                                                                   | Clerk's account management UI for profile and security settings. |
| [Kanban Board](https://shadcn-dashboard.kiranism.dev/dashboard/kanban)                                                                                                | Drag-and-drop task board built with dnd-kit and Zustand. Column sorting, priority badges, assignees, and due dates. |
| [Chat](https://shadcn-dashboard.kiranism.dev/dashboard/chat)                                                                                                          | Messaging UI with a conversation list, message bubbles, quick replies, attachments, and an auto-reply demo. Multi-panel layout that works on mobile. |
| [AI Chat](https://shadcn-dashboard.kiranism.dev/dashboard/ai-chat)                                                                                                    | Scripted AI chat that streams a predefined conversation through the real `useChat` lifecycle — no model, API route, or key. Built with the shadcn chat components (MessageScroller, Bubble, Marker). |
| [Notifications](https://shadcn-dashboard.kiranism.dev/dashboard/notifications)                                                                                        | Notification center with a header badge, popover preview, and a full page with All / Unread / Read tabs. Includes mark-as-read and mark-all-as-read. |
| [Workspaces](https://shadcn-dashboard.kiranism.dev/dashboard/workspaces)                                                                                              | Organization management using Clerk's `<OrganizationList />`. View, create, and switch between organizations. |
| [Team Management](https://shadcn-dashboard.kiranism.dev/dashboard/workspaces/team)                                                                                    | Team management using Clerk's `<OrganizationProfile />`. Manage members, roles, permissions, security, and org details. Needs an active organization. |
| [Billing & Plans](https://shadcn-dashboard.kiranism.dev/dashboard/billing)                                                                                            | Billing page using Clerk's `<PricingTable />`. View plans, subscribe, and manage subscriptions. Needs an active organization. |
| [Exclusive Page](https://shadcn-dashboard.kiranism.dev/dashboard/exclusive)                                                                                           | Plan-based access control with Clerk's `<Show>`. Only available to organizations on the Pro plan, with a fallback UI for everyone else. |
| [Not Found](https://shadcn-dashboard.kiranism.dev/dashboard/notfound)                                                                                                 | A root-level not-found page.                                                                                                                                                        |
| [Global Error](https://sentry.io/for/nextjs/?utm_source=github&utm_medium=paid-community&utm_campaign=general-fy26q2-nextjs&utm_content=github-banner-project-tryfree) | A shared error page wired to Sentry for logging, reports, and session replay. |

## Folder Structure

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Getting Started

> [!NOTE]
> This starter uses Next.js 16 (App Router) with React 19 and shadcn/ui. To run it locally:

Clone the repo:

```
git clone https://github.com/Kiranism/next-shadcn-dashboard-starter.git
```

- `bun install`
- Copy the example env file: `cp env.example.txt .env.local`
- Fill in the required variables in `.env.local`
- `bun run dev`

##### Environment variables

See `env.example.txt` for the variables you need. They cover authentication and error tracking.

##### Clerk setup

For setting up Clerk auth (including organizations, workspaces, and teams), see [clerk_setup.md](./docs/clerk_setup.md).

The app should now be running at http://localhost:3000.

> [!WARNING]
> After cloning or forking, be careful when pulling the latest changes. Updates can cause merge conflicts.

---

## Cleanup Script: Start Minimal in 60 Seconds

Most starters make you hand-delete demo pages and rip out dependencies. This one ships with a cleanup script that removes the optional features you don't need (folders, files, dependencies, docs, and env entries), leaving a minimal base to build on. Run `--list` to see what's removable:

```bash
bun run cleanup --interactive    # interactive mode
bun run cleanup --list           # see available features
bun run cleanup --dry-run chat   # preview before removing
bun run cleanup kanban chat      # remove specific features
```

Run `bun run cleanup --help` for all options (with npm, pass flags after `--`: `npm run cleanup -- --list`). The replacement files it writes live in `scripts/cleanup-templates/` as real, typechecked code. When you're done, delete `scripts/cleanup.js`, `scripts/cleanup-templates/`, and the `cleanup` entry in `package.json`.

## FAQ

**Is it production ready?**
Yes. Every feature is a complete, working implementation: authentication, CRUD flows, table search/filter/sort/pagination, and form validation with mutations all function end-to-end. It's a starting point for real applications, not a visual mockup.

**How is this different from other dashboard templates?**
Most dashboard templates are static demo boilerplates: screens that look finished but need rebuilding once you wire in real data. Here the tables, forms, auth, organizations, and billing all work end-to-end, the implementations follow official TanStack and Next.js patterns, and a cleanup script keeps the base minimal so you tweak it to your use case instead of deleting code.

**Is it free for commercial use?**
Yes. MIT-licensed and free for both personal and commercial projects: no paid tier, no license keys.

**Can I use it without Clerk?**
Yes. Run `bun run cleanup clerk` to remove Clerk authentication (along with organizations and billing) and wire in your own auth solution.

**How do I remove demo pages or features I don't need?**
Run `bun run cleanup --interactive` and pick what to strip, or `bun run cleanup --list` to see what can be removed.

**Does it support Next.js 16, React 19, and Tailwind CSS v4?**
Yes. The template is built on Next.js 16 (App Router), React 19, and Tailwind CSS v4, with shadcn/ui on Base UI primitives, and is actively maintained to track new releases.

**Can I use npm instead of Bun?**
Yes. Bun is preferred, but npm works too, and the repo even ships both Node.js and Bun Dockerfiles for deployment.

**Does it work with AI coding assistants?**
Yes. The repo ships AGENTS.md and CLAUDE.md with the project's conventions, plus a bundled Claude Code skill (`.claude/skills/kiranism-shadcn-dashboard`) that teaches agents how to add pages, tables, forms, and navigation the template way. Works with Claude Code, Cursor, and any tool that reads AGENTS.md.

**What data fetching pattern does it use?**
TanStack React Query with the official SSR pattern: `prefetchQuery` on the server, `HydrationBoundary` with `dehydrate` for hydration, and `useSuspenseQuery` on the client, plus nuqs for URL-synced search-param state. Mutations invalidate the cache on success.

**How do I deploy it?**
Deploy to Vercel out of the box, or use the included Docker setups: a Node.js Dockerfile and a Bun Dockerfile, both using Next.js standalone output mode. See the [deployment guide](./docs/deployment.md).

## Deploy

Deploy to Vercel out of the box, or use the included Docker setups: a Node.js Dockerfile and a Bun Dockerfile, both using Next.js standalone output mode. Full guide: [docs/deployment.md](./docs/deployment.md).

### Support

If this template saved you some time, a star is appreciated. You can also [buy me a coffee](https://buymeacoffee.com/kir4n) if you'd like.

[](https://buymeacoffee.com/kir4n)

---

### Overview (`.agents/skills/vercel-composition-patterns/README.md`)

# React Composition Patterns

A structured repository for React composition patterns that scale. These
patterns help avoid boolean prop proliferation by using compound components,
lifting state, and composing internals.

## Structure

- `rules/` - Individual rule files (one per rule)
  - `_sections.md` - Section metadata (titles, impacts, descriptions)
  - `_template.md` - Template for creating new rules
  - `area-description.md` - Individual rule files
- `metadata.json` - Document metadata (version, organization, abstract)
- **`AGENTS.md`** - Compiled output (generated)

## Rules

### Component Architecture (CRITICAL)

- `architecture-avoid-boolean-props.md` - Don't add boolean props to customize
  behavior
- `architecture-compound-components.md` - Structure as compound components with
  shared context

### State Management (HIGH)

- `state-lift-state.md` - Lift state into provider components
- `state-context-interface.md` - Define clear context interfaces
  (state/actions/meta)
- `state-decouple-implementation.md` - Decouple state management from UI

### Implementation Patterns (MEDIUM)

- `patterns-children-over-render-props.md` - Prefer children over renderX props
- `patterns-explicit-variants.md` - Create explicit component variants

## Core Principles

1. **Composition over configuration** — Instead of adding props, let consumers
   compose
2. **Lift your state** — State in providers, not trapped in components
3. **Compose your internals** — Subcomponents access context, not props
4. **Explicit variants** — Create ThreadComposer, EditComposer, not Composer
   with isThread

## Creating a New Rule

1. Copy `rules/_template.md` to `rules/area-description.md`
2. Choose the appropriate area prefix:
   - `architecture-` for Component Architecture
   - `state-` for State Management
   - `patterns-` for Implementation Patterns
3. Fill in the frontmatter and content
4. Ensure you have clear examples with explanations

## Impact Levels

- `CRITICAL` - Foundational patterns, prevents unmaintainable code
- `HIGH` - Significant maintainability improvements
- `MEDIUM` - Good practices for cleaner code

---

### Overview (`.agents/skills/vercel-react-best-practices/README.md`)

# React Best Practices

A structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.

## Structure

- `rules/` - Individual rule files (one per rule)
  - `_sections.md` - Section metadata (titles, impacts, descriptions)
  - `_template.md` - Template for creating new rules
  - `area-description.md` - Individual rule files
- `src/` - Build scripts and utilities
- `metadata.json` - Document metadata (version, organization, abstract)
- **`AGENTS.md`** - Compiled output (generated)
- **`test-cases.json`** - Test cases for LLM evaluation (generated)

## Getting Started

1. Install dependencies:

   ```bash
   pnpm install
   ```

2. Build AGENTS.md from rules:

   ```bash
   pnpm build
   ```

3. Validate rule files:

   ```bash
   pnpm validate
   ```

4. Extract test cases:
   ```bash
   pnpm extract-tests
   ```

## Creating a New Rule

1. Copy `rules/_template.md` to `rules/area-description.md`
2. Choose the appropriate area prefix:
   - `async-` for Eliminating Waterfalls (Section 1)
   - `bundle-` for Bundle Size Optimization (Section 2)
   - `server-` for Server-Side Performance (Section 3)
   - `client-` for Client-Side Data Fetching (Section 4)
   - `rerender-` for Re-render Optimization (Section 5)
   - `rendering-` for Rendering Performance (Section 6)
   - `js-` for JavaScript Performance (Section 7)
   - `advanced-` for Advanced Patterns (Section 8)
3. Fill in the frontmatter and content
4. Ensure you have clear examples with explanations
5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json

## Rule File Structure

Each rule file should follow this structure:

````markdown
---
title: Rule Title Here
impact: MEDIUM
impactDescription: Optional description
tags: tag1, tag2, tag3
---

## Rule Title Here

Brief explanation of the rule and why it matters.

**Incorrect (description of what's wrong):**

```typescript
// Bad code example
```
````

**Correct (description of what's right):**

```typescript
// Good code example
```

Optional explanatory text after examples.

Reference: [Link](https://example.com)

## File Naming Convention

- Files starting with `_` are special (excluded from build)
- Rule files: `area-description.md` (e.g., `async-parallel.md`)
- Section is automatically inferred from filename prefix
- Rules are sorted alphabetically by title within each section
- IDs (e.g., 1.1, 1.2) are auto-generated during build

## Impact Levels

- `CRITICAL` - Highest priority, major performance gains
- `HIGH` - Significant performance improvements
- `MEDIUM-HIGH` - Moderate-high gains
- `MEDIUM` - Moderate performance improvements
- `LOW-MEDIUM` - Low-medium gains
- `LOW` - Incremental improvements

## Scripts

- `pnpm build` - Compile rules into AGENTS.md
- `pnpm validate` - Validate all rule files
- `pnpm extract-tests` - Extract test cases for LLM evaluation
- `pnpm dev` - Build and validate

## Contributing

When adding or modifying rules:

1. Use the correct filename prefix for your section
2. Follow the `_template.md` structure
3. Include clear bad/good examples with explanations
4. Add appropriate tags
5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json
6. Rules are automatically sorted by title - no need to manage numbers!

## Acknowledgments

Originally created by [@shuding](https://x.com/shuding) at [Vercel](https://vercel.com).

---

### Overview (`.claude/skills/vercel-composition-patterns/README.md`)

# React Composition Patterns

A structured repository for React composition patterns that scale. These
patterns help avoid boolean prop proliferation by using compound components,
lifting state, and composing internals.

## Structure

- `rules/` - Individual rule files (one per rule)
  - `_sections.md` - Section metadata (titles, impacts, descriptions)
  - `_template.md` - Template for creating new rules
  - `area-description.md` - Individual rule files
- `metadata.json` - Document metadata (version, organization, abstract)
- **`AGENTS.md`** - Compiled output (generated)

## Rules

### Component Architecture (CRITICAL)

- `architecture-avoid-boolean-props.md` - Don't add boolean props to customize
  behavior
- `architecture-compound-components.md` - Structure as compound components with
  shared context

### State Management (HIGH)

- `state-lift-state.md` - Lift state into provider components
- `state-context-interface.md` - Define clear context interfaces
  (state/actions/meta)
- `state-decouple-implementation.md` - Decouple state management from UI

### Implementation Patterns (MEDIUM)

- `patterns-children-over-render-props.md` - Prefer children over renderX props
- `patterns-explicit-variants.md` - Create explicit component variants

## Core Principles

1. **Composition over configuration** — Instead of adding props, let consumers
   compose
2. **Lift your state** — State in providers, not trapped in components
3. **Compose your internals** — Subcomponents access context, not props
4. **Explicit variants** — Create ThreadComposer, EditComposer, not Composer
   with isThread

## Creating a New Rule

1. Copy `rules/_template.md` to `rules/area-description.md`
2. Choose the appropriate area prefix:
   - `architecture-` for Component Architecture
   - `state-` for State Management
   - `patterns-` for Implementation Patterns
3. Fill in the frontmatter and content
4. Ensure you have clear examples with explanations

## Impact Levels

- `CRITICAL` - Foundational patterns, prevents unmaintainable code
- `HIGH` - Significant maintainability improvements
- `MEDIUM` - Good practices for cleaner code

---

### Overview (`.claude/skills/vercel-react-best-practices/README.md`)

# React Best Practices

A structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.

## Structure

- `rules/` - Individual rule files (one per rule)
  - `_sections.md` - Section metadata (titles, impacts, descriptions)
  - `_template.md` - Template for creating new rules
  - `area-description.md` - Individual rule files
- `src/` - Build scripts and utilities
- `metadata.json` - Document metadata (version, organization, abstract)
- **`AGENTS.md`** - Compiled output (generated)
- **`test-cases.json`** - Test cases for LLM evaluation (generated)

## Getting Started

1. Install dependencies:

   ```bash
   pnpm install
   ```

2. Build AGENTS.md from rules:

   ```bash
   pnpm build
   ```

3. Validate rule files:

   ```bash
   pnpm validate
   ```

4. Extract test cases:
   ```bash
   pnpm extract-tests
   ```

## Creating a New Rule

1. Copy `rules/_template.md` to `rules/area-description.md`
2. Choose the appropriate area prefix:
   - `async-` for Eliminating Waterfalls (Section 1)
   - `bundle-` for Bundle Size Optimization (Section 2)
   - `server-` for Server-Side Performance (Section 3)
   - `client-` for Client-Side Data Fetching (Section 4)
   - `rerender-` for Re-render Optimization (Section 5)
   - `rendering-` for Rendering Performance (Section 6)
   - `js-` for JavaScript Performance (Section 7)
   - `advanced-` for Advanced Patterns (Section 8)
3. Fill in the frontmatter and content
4. Ensure you have clear examples with explanations
5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json

## Rule File Structure

Each rule file should follow this structure:

````markdown
---
title: Rule Title Here
impact: MEDIUM
impactDescription: Optional description
tags: tag1, tag2, tag3
---

## Rule Title Here

Brief explanation of the rule and why it matters.

**Incorrect (description of what's wrong):**

```typescript
// Bad code example
```
````

**Correct (description of what's right):**

```typescript
// Good code example
```

Optional explanatory text after examples.

Reference: [Link](https://example.com)

## File Naming Convention

- Files starting with `_` are special (excluded from build)
- Rule files: `area-description.md` (e.g., `async-parallel.md`)
- Section is automatically inferred from filename prefix
- Rules are sorted alphabetically by title within each section
- IDs (e.g., 1.1, 1.2) are auto-generated during build

## Impact Levels

- `CRITICAL` - Highest priority, major performance gains
- `HIGH` - Significant performance improvements
- `MEDIUM-HIGH` - Moderate-high gains
- `MEDIUM` - Moderate performance improvements
- `LOW-MEDIUM` - Low-medium gains
- `LOW` - Incremental improvements

## Scripts

- `pnpm build` - Compile rules into AGENTS.md
- `pnpm validate` - Validate all rule files
- `pnpm extract-tests` - Extract test cases for LLM evaluation
- `pnpm dev` - Build and validate

## Contributing

When adding or modifying rules:

1. Use the correct filename prefix for your section
2. Follow the `_template.md` structure
3. Include clear bad/good examples with explanations
4. Add appropriate tags
5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json
6. Rules are automatically sorted by title - no need to manage numbers!

## Acknowledgments

Originally created by [@shuding](https://x.com/shuding) at [Vercel](https://vercel.com).

---

### Clerk Setup (`docs/clerk_setup.md`)

# Clerk Setup Guide

This guide covers the setup and configuration of Clerk features used in this starter template.

## Clerk Scopes Required

- **Authentication** - User sign-in/sign-up and session management
- **Organizations** - Multi-tenant workspace management (see setup below)
- **Billing** - Organization-level subscription management (see setup below)

## Clerk Organizations Setup (Workspaces & Teams)

This starter kit includes multi-tenant workspace management powered by **Clerk Organizations**. To enable this feature:

### Enable Organizations in Clerk Dashboard:

1. Go to [Clerk Dashboard](https://dashboard.clerk.com)
2. Navigate to **configure**
3. Click **Organizations settings**
4. Configure default roles if needed in the roles and permissions.

### Server-Side Permission Checks:

- This starter follows [Clerk's recommended patterns](https://clerk.com/blog/how-to-build-multitenant-authentication-with-clerk)

### Navigation RBAC System:

- Fully client-side navigation filtering using `useNav` hook
- Supports `requireOrg`, `permission`, and `role` checks (all client-side, instant)
- Configured in `src/config/nav-config.ts` with `access` properties
- See `docs/nav-rbac.md` for detailed documentation

### For more information, see:

- [Clerk Organizations documentation](https://clerk.com/docs/organizations/overview)
- [Multi-tenant authentication guide](https://clerk.com/blog/how-to-build-multitenant-authentication-with-clerk)

## Clerk Billing Setup (Organization Subscriptions)

This starter kit includes **Clerk Billing for B2B** to manage organization-level subscriptions. Plans and features are managed through the Clerk Dashboard, and the application checks access using Clerk's `has()` function.

> [!WARNING]
> Billing is currently in Beta and its APIs are experimental and may undergo breaking changes. To mitigate potential disruptions, we recommend pinning your SDK and `clerk-js` package versions.

### Key Features:

- Organization-level subscription management
- Plan-based access control using the `<Show>` component
- Feature-based authorization
- Integrated Stripe payment processing
- Server-side plan/feature checks using `has()` function

### Billing Cost Structure:

Clerk Billing costs **0.7% per transaction**, plus transaction fees which are paid directly to Stripe. Clerk Billing is **not** the same as Stripe Billing. Plans and pricing are managed directly through the Clerk Dashboard and won't sync with your existing Stripe products or plans. Clerk uses Stripe **only** for payment processing, so you don't need to set up Stripe Billing.

### Setup Instructions:

#### 1. Enable Billing:

- Navigate to [Billing Settings](https://dashboard.clerk.com/~/billing/settings) in the Clerk Dashboard
- Enable billing for your application
- Choose payment gateway:
  - **Clerk development gateway**: A shared **test** Stripe account for development instances. This allows developers to test and build Billing flows **in development** without needing to create and configure a Stripe account.
  - **Stripe account**: Use your own Stripe account for production. **A Stripe account created for a development instance cannot be used for production**. You will need to create a separate Stripe account for your production environment.

#### 2. Create Plans:

- Navigate to [Plans page](https://dashboard.clerk.com/~/billing/plans) in the Clerk Dashboard
- Select **Plans for Organizations** tab
- Click **Add Plan** and create plans (e.g., `free`, `pro`, `team`)
- Set pricing and billing intervals
- Toggle **Publicly available** to show in `<PricingTable />` and `<OrganizationProfile />` components

#### 3. Add Features to Plans:

- You can add Features when creating a Plan, or add them later:
  1. Navigate to the [Plans](https://dashboard.clerk.com/~/billing/plans) page
  2. Select the Plan you'd like to add a Feature to
  3. In the **Features** section, select **Add Feature**
- Feature names in Clerk Dashboard should match what you check in code

#### 4. Usage in Code:

**Server-side checks using `has()`:**

```typescript
// Check if organization has a Plan
const hasPremiumAccess = has({ plan: 'gold' });

// Check if organization has a Feature
const hasPremiumAccess = has({ feature: 'widgets' });
```

The `has()` method is available on the auth object and checks if the Organization has been granted a specific type of access control (Role, Permission, Feature, or Plan) and returns a boolean value.

**Client-side protection using `<Show>`:**

```tsx
<Show
  when={{ plan: 'bronze' }}
  fallback={<p>Only subscribers to the Bronze plan can access this content.</p>}
>
  <h1>Exclusive Bronze Content</h1>
</Show>
```

Or protect by Feature:

```tsx
<Show
  when={{ feature: 'premium_access' }}
  fallback={<p>Only subscribers with the Premium Access feature can access this content.</p>}
>
  <h1>Exclusive Premium Content</h1>
</Show>
```

---

### Deployment (`docs/deployment.md`)

# Deployment

The starter deploys to Vercel out of the box, or anywhere Docker runs. `next.config.ts` sets `output: 'standalone'`, so production builds are optimized for self-hosting.

## Vercel (Recommended)

1. Connect the repository to Vercel
2. Add environment variables in the dashboard
3. Deploy

For other platforms, see the [Next.js deployment docs](https://nextjs.org/docs/app/getting-started/deploying).

## Environment Variables for Production

Ensure these are set in your deployment platform:

- `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY`
- `CLERK_SECRET_KEY`
- All `NEXT_PUBLIC_*` variables for client-side access
- `SENTRY_*` variables if using error tracking

Sentry source maps are uploaded automatically in CI.

## Docker

Two production-ready Dockerfiles are included: `Dockerfile` (Node.js) and `Dockerfile.bun` (Bun). Pass `NEXT_PUBLIC_*` variables as `--build-arg` at build time and runtime secrets via `-e` at run time.

Build the image:

```bash
# Node.js
docker build \
  --build-arg NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_xxxxx \
  -t shadcn-dashboard .

# OR Bun
docker build -f Dockerfile.bun \
  --build-arg NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_xxxxx \
  -t shadcn-dashboard .
```

Run the container:

```bash
docker run -d -p 3000:3000 \
  -e NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_xxxxx \
  -e CLERK_SECRET_KEY=sk_live_xxxxx \
  --restart unless-stopped \
  --name shadcn-dashboard \
  shadcn-dashboard
```

---

### Forms (`docs/forms.md`)

# Forms

Forms follow the **official shadcn TanStack Form conventions**, scaled with
TanStack's own [`createFormHook`](https://tanstack.com/form/latest/docs/framework/react/guides/form-composition)
pattern: the doc's `Field` anatomy is written **once per widget** as a reusable
field component, and pages use them as one-liners inside `form.AppField`.

- [shadcn: TanStack Form](https://ui.shadcn.com/docs/forms/tanstack) — the
  anatomy inside every field component
- [TanStack Form docs](https://tanstack.com/form/latest) — validators,
  listeners, arrays, async validation

## Architecture

| File | What it provides |
| --- | --- |
| `src/lib/form-context.ts` | `createFormHookContexts` — `fieldContext`, `formContext`, `useFieldContext`, `useFieldInvalid`, `BaseFieldProps` |
| `src/components/forms/fields/*.tsx` | 16 field components, each the exact shadcn doc anatomy for its widget |
| `src/components/forms/submit-button.tsx` | `SubmitButton` — disables while submitting (`form.Subscribe`) |
| `src/lib/form.ts` | `createFormHook` — exports `useAppForm` / `withForm` with everything registered |

## The pattern

One `useAppForm` per form, a Zod schema validated on submit, and one
`form.AppField` per field rendering the matching component:

```tsx
'use client';

import { useAppForm } from '@/lib/form';
import { FieldGroup } from '@/components/ui/field';
import * as z from 'zod';

const formSchema = z.object({
  title: z.string().min(5, 'Title must be at least 5 characters.'),
  severity: z.string().min(1, 'Select a severity.')
});

export function BugReportForm() {
  const form = useAppForm({
    defaultValues: { title: '', severity: '' },
    validators: { onSubmit: formSchema },
    onSubmit: ({ value }) => console.log(value)
  });

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        form.handleSubmit();
      }}
    >
      <FieldGroup>
        <form.AppField
          name='title'
          children={(field) => (
            <field.TextField label='Bug Title' required placeholder='Login button broken' />
          )}
        />
        <form.AppField
          name='severity'
          children={(field) => (
            <field.SelectField
              label='Severity'
              options={[
                { value: 'low', label: 'Low' },
                { value: 'high', label: 'High' }
              ]}
            />
          )}
        />
        <form.AppForm>
          <form.SubmitButton>Submit</form.SubmitButton>
        </form.AppForm>
      </FieldGroup>
    </form>
  );
}
```

`form.AppField`'s `name` is fully typed against `defaultValues` — typos are
compile errors. Field-level validators/listeners go on the `form.AppField`
element (async checks, `onChangeListenTo` linked fields).

## Available field components

All render inside `form.AppField` as `field.XxxField`; every one takes
`label`, `description?`, `required?`.

| Component | Value type | Notes |
| --- | --- | --- |
| `TextField` | `string` / `number` | Any input `type` (text, email, password, tel, url, time, number). Number inputs convert at the edge — clearing writes `undefined`. Shows a spinner while async validators run. |
| `TextareaField` | `string` | `showCount` renders a character counter against `maxLength` |
| `SelectField` | `string` | `options` array |
| `CheckboxField` | `boolean` | Single checkbox (terms, consent) |
| `SwitchField` | `boolean` | Label/description left, switch right |
| `RadioGroupField` | `string` | `FieldSet` + `FieldLegend` semantics |
| `SliderField` | `number` | `min`/`max`/`step` + value readout |
| `ComboboxField` | `string` | Searchable select (Popover + Command) |
| `DatePickerField` | `Date \| undefined` | Popover + Calendar, `disabledDates` |
| `DateRangeField` | `DateRange \| undefined` | Two-month range calendar |
| `OtpField` | `string` | 6-digit code (3 + 3) |
| `ColorField` | `string` | Native picker + hex input |
| `FileUploadField` | `File[]` | Wraps `FileUploader`, `maxSize`/`maxFiles` |
| `CheckboxGroupField` | `string[]` | Needs `mode='array'` on the AppField |
| `TagsField` | `string[]` | Needs `mode='array'`; Enter/Add pushes, badges remove |
| `ToggleGroupField` | `string[]` | Needs `mode='array'`; pass `ToggleGroupItem`s as children |

## One-off custom fields — drop down to `form.Field`

For anything the components don't cover (object-row arrays, bespoke UI), use
the raw doc pattern directly — it composes freely with the components:

```tsx
<form.Field
  name='members'
  mode='array'
  children={(field) => (
    <>
      {field.state.value.map((_, i) => (
        <form.Field
          name={`members[${i}].name`}
          children={(subField) => {
            const isInvalid = subField.state.meta.isTouched && !subField.state.meta.isValid;
            return (
              <Field data-invalid={isInvalid}>
                <Input
                  value={subField.state.value}
                  onChange={(e) => subField.handleChange(e.target.value)}
                  onBlur={subField.handleBlur}
                  aria-invalid={isInvalid}
                />
                {isInvalid && <FieldError errors={subField.state.meta.errors} />}
              </Field>
            );
          }}
        />
      ))}
      <Button type='button' onClick={() => field.pushValue({ name: '' })}>Add</Button>
    </>
  )}
/>
```

The doc conventions inside any custom field: `data-invalid` on `<Field>`,
`aria-invalid` on the control, `{isInvalid && <FieldError errors={…} />}`,
function validators return `{ message: '…' }` objects.

## Scaling to large forms — `withForm` sections

Split a big form into reusable section components with `withForm` (also
exported from `@/lib/form`). Sections receive the form instance as a prop and
keep **fully typed field names** — a typo'd `name` inside a section is still a
compile error:

```tsx
import { useAppForm, withForm } from '@/lib/form';

const ShippingSection = withForm({
  defaultValues: checkoutDefaults, // ties the section to the form's shape
  render: function ShippingRender({ form }) {
    return (
      <FieldGroup>
        <form.AppField
          name='shipping.street'
          children={(field) => <field.TextField label='Street' required />}
        />
        <form.AppField
          name='shipping.city'
          children={(field) => <field.TextField label='City' required />}
        />
      </FieldGroup>
    );
  }
});

// In the page:
const form = useAppForm({ defaultValues: checkoutDefaults, ... });
<ShippingSection form={form} />
```

Deep paths (`org.billing.address.city`), array sub-paths
(`admins[0].prefs.notify`), and union-typed leaves all stay typed, and
typechecking stays fast at 40+ fields.

## Template-specific notes

**Submitting with React Query.** `onSubmit` awaits the mutation; success/error
handling lives on the mutation (see `features/products/components/product-form.tsx`):

```tsx
onSubmit: async ({ value }) => {
  await createMutation.mutateAsync(value);
};
```

**Sheet / Dialog forms.** The submit button lives in the footer, outside the
`<form>` element, connected via the HTML `form` attribute
(`features/users/components/user-form-sheet.tsx`):

```tsx
<form id='user-form-sheet' onSubmit={…}>…</form>
<SheetFooter>
  <Button type='submit' form='user-form-sheet'>Save</Button>
</SheetFooter>
```

**Multi-step forms.** `useFormStepper(stepSchemas, { fullSchema })` from
`@/hooks/use-stepper` gates step navigation: `Next` validates the current
step's schema and paints its errors; the final submit re-validates the whole
schema and never submits invalid data. Route every submit through the gate —
see `features/forms/components/multi-step-product-form.tsx`.

**Number inputs.** `TextField type='number'` already converts at the edge;
give required numbers a human message: `z.number({ error: 'Price is required' })`.

**Caveats to know** (verified by stress testing):

- `field.XxxField` components assert their value type via
  `useFieldContext<T>()` — the compiler checks the `name` path exists, but
  not that the widget matches the path's value type (a `SwitchField` on a
  string path compiles and renders wrong values). Match widgets to the table
  above.
- Rendering a field component outside `form.AppField` throws a clear error
  (`fieldContext only works when within a fieldComponent…`) — it cannot fail
  silently.
- Two forms with identical field names mounted at once (a sheet over a page)
  produce duplicate `id` attributes — the shadcn doc's `id={field.name}`
  convention. Form *state* stays fully isolated; only label-target ids
  collide. Rename fields or avoid simultaneous mounting if labels must stay
  clickable in both.
- Forgetting `mode='array'` on an AppField using `CheckboxGroupField` /
  `TagsField` / `ToggleGroupField` still renders and updates — but keep the
  convention: array mode gives TanStack correct per-item meta tracking.

## Examples in the dashboard

| Page | Route | Demonstrates |
| --- | --- | --- |
| Basic Form | `/dashboard/forms/basic` | All 16 field components incl. array-mode groups, pickers, OTP, tags, upload |
| Advanced | `/dashboard/forms/advanced` | Async validation, linked fields (`onChangeListenTo`), nested paths, raw `form.Field` object-row arrays, listener side effects |
| Multi-Step | `/dashboard/forms/multi-step` | Per-step schemas, validation gate, review step |
| Sheet Form | `/dashboard/forms/sheet-form` | Sheet + Dialog forms with external submit buttons |

---

### Nav Rbac (`docs/nav-rbac.md`)

# Simplified Navigation RBAC System

## Overview

This document explains the fully client-side RBAC (Role-Based Access Control) system for navigation items.

**Key Insight**: Navigation visibility is UX only, not security. We can check everything client-side using Clerk's hooks!

## Architecture

### Core Files

1. **`src/hooks/use-nav.ts`** - Single hook that handles all filtering logic (fully client-side)
2. **`src/types/index.ts`** - Type definitions with `access` property

### Why Client-Side?

- **Navigation visibility is UX only** - Users can't bypass security by seeing/hiding nav items
- **Clerk provides all data client-side** - `useOrganization()` gives us `membership.permissions` and `membership.role`
- **Zero server calls** - Instant filtering, no loading states, no UI flashing
- **Better performance** - No network latency, no async complexity

**Note**: For actual security (API routes, server actions, page protection), always use server-side checks.

## Performance Characteristics

### All Checks Are Synchronous

✅ **requireOrg**: Client-side check using `useOrganization()`  
✅ **permission**: Client-side check using `membership.permissions` array  
✅ **role**: Client-side check using `membership.role`  
⚠️ **plan/feature**: Requires server-side check (see below)

### Zero Server Calls

- All navigation filtering happens synchronously
- No loading states
- No UI flashing
- Instant results

## Usage

### In `nav-config.ts`

```typescript
{
  title: 'Teams',
  url: '/dashboard/workspaces/team',
  icon: 'userPen',
  // Simple: requireOrg (client-side check, instant)
  access: { requireOrg: true }
}

{
  title: 'Admin Panel',
  url: '/dashboard/admin',
  icon: 'settings',
  // All client-side checks - instant!
  access: {
    requireOrg: true,
    permission: 'org:admin:manage',  // Client-side from membership.permissions
    role: 'admin'  // Client-side from membership.role
}
```

### In Components

```typescript
import { useFilteredNavItems } from '@/hooks/use-nav';

function MyComponent() {
  const filteredItems = useFilteredNavItems(navItems);
  // filteredItems is automatically filtered based on RBAC
}
```

### Plan/Feature Checks

Plans and features require Clerk's `has()` function which is server-side only. Options:

1. **Store in organization metadata** (recommended for navigation):

   ```typescript
   // In your organization setup
   organization.publicMetadata.plan = 'pro';

   // In nav-config.ts
   access: {
     requireOrg: true,
     // Check metadata instead of plan
   }
   ```

2. **Show item, protect at page level** (current approach):
   - Navigation item is shown
   - Page component checks server-side and redirects/shows error if needed

3. **Use server action** (if you really need it):
   - Only for navigation items that absolutely need plan/feature checks
   - Most navigation items won't need this

## Scalability

### Adding New Items

Just add to `nav-config.ts`:

```typescript
{
  title: 'New Feature',
  url: '/dashboard/new',
  icon: 'star',
  access: { plan: 'pro' }  // That's it!
}
```

The system automatically:

- Filters it in sidebar
- Filters it in kbar
- Handles async checks if needed
- Handles sync checks immediately

### Adding New Access Types

1. Add to `PermissionCheck` interface in `src/app/actions/rbac.ts`
2. Add check logic in `checkAccess()` function
3. Update `use-nav.ts` to handle the new type

## Comparison: Before vs After

### Before (Overcomplicated)

- 4 files with complex logic
- Multiple hooks and utilities
- Unclear data flow
- Potential for bugs

### After (Simplified)

- 1 main hook file
- Clear, linear logic
- Easy to understand
- Easy to maintain

## Best Practices

1. **Use `requireOrg: true` for simple cases** - It's instant and requires no server call
2. **Combine checks when possible** - `{ requireOrg: true, permission: '...' }` is more efficient than separate checks
3. **Avoid unnecessary checks** - Don't add `access` if the item should always be visible

## Migration from Old System

The old `visible` function still works for backward compatibility:

```typescript
// Old way (still works)
visible: (context) => !!context?.organization;

// New way (recommended)
access: {
  requireOrg: true;
}
```

## Future Improvements

Potential optimizations if needed:

1. Cache permission checks (e.g., React Query)
2. Prefetch permissions on app load
3. Optimistic UI updates

But for now, the current implementation is:

- ✅ Simple
- ✅ Fast
- ✅ Scalable
- ✅ Maintainable

---

### Themes (`docs/themes.md`)

# Adding New Themes

This guide explains how to add a new theme to the application. The theme system uses CSS custom properties with `[data-theme]` selectors for easy theme switching.

## The Journey: Adding a New Theme

When adding a new theme, follow this journey:

1. **Create theme CSS file** → `src/styles/themes/your-theme-name.css` with `[data-theme='your-theme-name']`
2. **Import theme** → Add `@import` to `src/styles/theme.css`
3. **Register theme** → Add to `THEMES` array in `src/components/themes/theme.config.ts`
4. **Add fonts (if needed)** → Import fonts in `src/components/themes/font.config.ts` if using custom Google Fonts
5. **Set as default (optional)** → Update `DEFAULT_THEME` in `src/components/themes/active-theme.tsx`

See the **Step-by-Step Guide** section below for detailed instructions.

## Quick Start: Set Your Theme as Default

To make your new theme the default (so it loads automatically without the theme switcher):

1. Open `src/components/themes/active-theme.tsx`
2. Change line 12: `const DEFAULT_THEME = 'your-theme-name';`
3. Save and restart your dev server

That's it! Your theme will now be the default for all new users.

> **Note:** Make sure you've completed steps 1-3 above before setting a theme as default.

## Theme Structure

All themes are located in `src/styles/themes/` directory. Each theme is a complete, self-contained CSS file that defines all design tokens for both light and dark modes.

## File Format

Each theme file must follow this structure:

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Step-by-Step Guide: Adding a New Theme

Follow these steps in order to add a new theme to your application.

### Step 1: Create Theme CSS File

Create a new file in `src/styles/themes/` with a descriptive name (use kebab-case):

```bash
src/styles/themes/your-theme-name.css
```

**Important:** The filename should match the `data-theme` attribute value you'll use in the CSS.

### Step 2: Define Your Theme with `[data-theme]` Attribute

Copy the structure from the "File Format" section above and fill in your color values. Use OKLCH color format for better color consistency:

```css
/* Light mode tokens */
[data-theme='your-theme-name'] {
  --background: oklch(1 0 0); /* White */
  --foreground: oklch(0.145 0 0); /* Dark gray */
  --card: oklch(...);
  /* ... all other tokens */
}

/* Dark mode tokens */
[data-theme='your-theme-name'].dark {
  --background: oklch(0.145 0 0); /* Dark */
  --foreground: oklch(0.985 0 0); /* Light */
  /* ... all other tokens with dark mode values */
}

/* Theme inline mappings for Tailwind */
[data-theme='your-theme-name'] {
  @theme inline {
    /* All the mappings as shown in the File Format section */
  }
}
```

**Color Format:**

- Use `oklch()` format: `oklch(lightness chroma hue)`
- Example: `oklch(0.852 0.199 91.936)` = light green-blue
- Lightness: 0-1 (0 = black, 1 = white)
- Chroma: 0+ (0 = grayscale, higher = more saturated)
- Hue: 0-360 (color wheel position)

**Key Points:**

- The `[data-theme='your-theme-name']` selector is what makes your theme work
- The value `'your-theme-name'` must match exactly in all places (CSS file, config, etc.)
- Always include both light and dark mode variants
- Include the `@theme inline` block for Tailwind CSS integration

### Step 3: Import Theme in theme.css

Add your theme import to `src/styles/theme.css`:

```css
@import './themes/your-theme-name.css';
```

This makes your theme available to the application.

### Step 4: Add Theme to theme.config.ts

Add your theme to the `THEMES` array in `src/components/themes/theme.config.ts`:

```typescript
export const THEMES = [
  // ... existing themes
  {
    name: 'Your Theme Name', // Display name in the UI
    value: 'your-theme-name' // Must match [data-theme] value exactly
  }
];
```

**Important:** The `value` field must match the `data-theme` attribute value from your CSS file exactly.

### Step 5: Add Custom Fonts (If Needed)

**Only do this step if your theme requires a custom Google Font that isn't already loaded.**

If you want to use a Google Font in your theme:

**File:** `src/components/themes/font.config.ts`

1. **Import the font** from `next/font/google`:

```typescript
import { Your_Font_Name } from 'next/font/google';
```

2. **Configure the font** with a CSS variable:

```typescript
const fontYourName = Your_Font_Name({
  subsets: ['latin'],
  weight: ['400', '500', '700'], // Adjust weights as needed
  variable: '--font-your-name' // Optional: custom variable name
});
```

3. **Add it to the `fontVariables` export**:

```typescript
export const fontVariables = cn(
  // ... existing fonts
  fontYourName.variable
);
```

4. **Use the font in your theme CSS** by its display name (not the CSS variable):

```css
[data-theme='your-theme-name'] {
  --font-sans: 'Your Font Name', sans-serif; /* Use the actual font name */
  --font-mono: 'Your Mono Font', monospace;
}
```

**Important Notes:**

- Use the font's **display name** in CSS (e.g., `'Geist'`, `'Architects Daughter'`), not the CSS variable
- The font must be imported in `font.config.ts` for it to be loaded by Next.js
- Font variables from `font.config.ts` are automatically applied to the body via `layout.tsx`
- You can use any Google Font available in `next/font/google`
- Check existing fonts in `font.config.ts` before adding new ones - you might be able to reuse them

**Example:** The `notebook` theme uses `Architects Daughter`:

- Imported in `font.config.ts` as `Architects_Daughter`
- Used in `notebook.css` as `'Architects Daughter'` (with quotes and space)

### Step 6: Set as Default Theme (Optional)

If you want your theme to be the default theme that loads when users first visit the application (without needing the theme switcher), update the default theme constant:

**File:** `src/components/themes/theme.config.ts`

```typescript
/**
 * Default theme that loads when no user preference is set
 * Change this value to set a different default theme
 */
export const DEFAULT_THEME = 'your-theme-name'; // Change from 'vercel' to your theme name
```

**Note:**

- This is the **single source of truth** for the default theme - it's automatically used in both server-side rendering and client-side code
- This will make your theme the default for all new users
- Existing users who have already selected a theme will still see their saved preference (stored in cookies)
- The default theme is applied immediately on page load (no flash of unstyled content)

### Step 7: Test Your Theme

1. Start your development server
2. Open the theme selector in the UI
3. Select your new theme
4. Verify it works in both light and dark modes
5. Test scaled variant by selecting "Your Theme Name (Scaled)"
6. If you set it as default, clear your browser cookies and refresh to see it load automatically

## Quick Reference: File Locations

When adding a new theme, you'll work with these files in this order:

1. ✅ `src/styles/themes/your-theme-name.css` - Create theme file with `[data-theme]` attribute
2. ✅ `src/styles/theme.css` - Import your theme file
3. ✅ `src/components/themes/theme.config.ts` - Add theme to `THEMES` array
4. ⚠️ `src/components/themes/font.config.ts` - Add fonts only if needed
5. ⚠️ `src/components/themes/active-theme.tsx` - Set as default only if desired

## Required Tokens

### Minimum Required

At minimum, your theme should define these tokens:

- `--background`
- `--foreground`
- `--card` & `--card-foreground`
- `--popover` & `--popover-foreground`
- `--primary` & `--primary-foreground`
- `--secondary` & `--secondary-foreground`
- `--muted` & `--muted-foreground`
- `--accent` & `--accent-foreground`
- `--destructive` & `--destructive-foreground`
- `--border`
- `--input`
- `--ring`
- `--radius`

### Optional Tokens

These can be omitted if not needed:

- `--chart-1` through `--chart-5` (defaults to primary colors)
- `--sidebar-*` tokens (defaults to card colors)
- `--font-*` tokens (uses system defaults)
- `--shadow-*` tokens (no shadows if omitted)
- `--tracking-normal` (no letter spacing if omitted)
- `--spacing` (uses default)

## Example: Complete Theme

See `src/styles/themes/claude.css` for a complete example with all tokens defined.

## Example: Minimal Theme

For a minimal theme, you can copy an existing theme and modify only the colors you want to change. The system will fall back to defaults for any missing tokens.

## Color Format Reference

### OKLCH Format

```
oklch(lightness chroma hue)
```

- **Lightness**: 0-1 (0 = black, 1 = white)
- **Chroma**: 0+ (0 = grayscale, 0.2+ = colorful)
- **Hue**: 0-360 degrees
  - 0/360 = Red
  - 60 = Yellow
  - 120 = Green
  - 180 = Cyan
  - 240 = Blue
  - 300 = Magenta

### Examples

```css
/* Pure white */
--background: oklch(1 0 0);

/* Pure black */
--foreground: oklch(0 0 0);

/* Bright blue */
--primary: oklch(0.7 0.2 240);

/* Muted gray */
--muted: oklch(0.5 0 0);
```

## Scaled Variants

All themes automatically support scaled variants. When a user selects "Theme Name (Scaled)", the `.theme-scaled` class is applied, which adjusts spacing and text sizes. No additional CSS is needed in your theme file.

## Best Practices

1. **Use descriptive theme names**: Use kebab-case (e.g., `ocean-blue`, `forest-green`)
2. **Provide both light and dark modes**: Always define both variants
3. **Test accessibility**: Ensure sufficient contrast between foreground and background
4. **Keep tokens consistent**: Use similar lightness/chroma values for related colors
5. **Document special features**: If your theme has unique characteristics (like no shadows or custom fonts), add comments

## Troubleshooting

### Theme Not Appearing

- Check that the file is imported in `src/styles/theme.css`
- Verify the theme name matches in both CSS file and theme-selector.tsx
- Ensure the file is saved and the dev server has reloaded

### Colors Not Applying

- Verify all required tokens are defined
- Check that `@theme inline` block includes all color mappings
- Ensure OKLCH format is correct (no typos)

### Dark Mode Not Working

- Verify `.dark` selector is correct: `[data-theme='name'].dark`
- Check that dark mode tokens are defined
- Ensure `next-themes` is properly configured

## Setting a Default Theme

By default, the application uses the `vercel` theme. To change the default theme that loads for new users:

### Change Default Theme Constant

Edit `src/components/themes/theme.config.ts` and update the `DEFAULT_THEME` constant:

```typescript
/**
 * Default theme that loads when no user preference is set
 * Change this value to set a different default theme
 */
export const DEFAULT_THEME = 'your-theme-name'; // Change this value
```

**How it works:**

- **Single source of truth**: `DEFAULT_THEME` is defined in `theme.config.ts` and imported everywhere it's needed
- **Server-side**: Applied immediately in the HTML `data-theme` attribute (no flash)
- **Client-side**: Used as fallback when no cookie preference exists
- **User preferences**: Still respects saved user preferences (stored in cookies)
- **Automatic**: No need to update multiple files - change it once and it works everywhere

**Benefits of this approach:**

✅ No code duplication - defined once, used everywhere  
✅ Type-safe - TypeScript ensures consistency  
✅ Easy to change - update one line in one file  
✅ Well-documented - clear comments explain its purpose  
✅ Immediate application - no flash of unstyled content

## Using Google Fonts in Themes

> **Note:** This section provides additional details about fonts. For the complete step-by-step process, see **Step 5** in the "Step-by-Step Guide" above.

### When to Add Fonts

You only need to add fonts to `font.config.ts` if:

- Your theme uses a Google Font that isn't already imported
- You want to use a custom font that requires loading

**Tip:** Check `src/components/themes/font.config.ts` first - many fonts may already be available!

### Font Loading Process

1. **Import the font** in `src/components/themes/font.config.ts`:

```typescript
import { Roboto, Roboto_Mono } from 'next/font/google';
```

2. **Configure the font** with a CSS variable:

```typescript
const fontRoboto = Roboto({
  subsets: ['latin'],
  weight: ['400', '500', '700'],
  variable: '--font-roboto'
});
```

3. **Add to fontVariables export**:

```typescript
export const fontVariables = cn(
  // ... existing fonts
  fontRoboto.variable
);
```

4. **Use in your theme CSS** with the font's display name:

```css
[data-theme='your-theme'] {
  --font-sans: 'Roboto', sans-serif; /* Use display name, not CSS variable */
  --font-mono: 'Roboto Mono', monospace;
}
```

### Important Notes

- **Font names**: Use the font's display name in CSS (e.g., `'Roboto'`, `'Open Sans'`), not the CSS variable name
- **Font loading**: Fonts must be imported in `font.config.ts` to be loaded by Next.js
- **Automatic application**: Font variables are automatically applied to the body element via `layout.tsx`
- **Available fonts**: Check [Next.js Font Optimization](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) for available Google Fonts

### Example: Notebook Theme

The `notebook` theme uses `Architects Daughter`:

**In `font.config.ts`:**

```typescript
import { Architects_Daughter } from 'next/font/google';

const fontArchitectsDaughter = Architects_Daughter({
  subsets: ['latin'],
  weight: '400',
  variable: '--font-architects-daughter'
});

export const fontVariables = cn(
  // ... other fonts
  fontArchitectsDaughter.variable
);
```

**In `notebook.css`:**

```css
[data-theme='notebook'] {
  --font-sans: 'Architects Daughter', sans-serif;
}
```

## Reference Files

- **Complete theme example**: `src/styles/themes/claude.css`
- **Theme aggregator**: `src/styles/theme.css`
- **Theme selector component**: `src/components/themes/theme-selector.tsx`
- **Theme provider**: `src/components/themes/active-theme.tsx`
- **Theme configuration** (includes default theme): `src/components/themes/theme.config.ts`
- **Font configuration**: `src/components/themes/font.config.ts`

---

--- METRICS ---
- Files Extracted: 10
- Estimated Token Budget: ~15590 tokens
- Canonical Reference: https://codewiki.google/github.com/Kiranism/next-shadcn-dashboard-starter
