next-shadcn-dashboard-starter Agent Skills & AI Rules

GitHub

Free, open source, AI-friendly admin dashboard template built with Next.js 16, shadcn/ui, Tailwind CSS, and TypeScript. Production-ready tables, forms,...

RAW Rules
234 Discovered Rules โ€ข ~197,706 Estimated Tokens
Quick:
Loading skill content...

AI Agent Protocol & Instructions

AGENTS.md root-instruction ~6,586 tokens
Raw
# AGENTS.md - AI Coding Agent Reference

This file provides essential information for AI coding agents working on this project. It contains project-specific details, conventions, and guidelines that complement the README.

---

## Project Overview

**Next.js Admin Dashboard Starter** is a production-ready admin dashboard template built with:

- **Framework**: Next.js 16 (App Router)
- **Language**: TypeScript 7
- **Styling**: Tailwind CSS v4
- **UI Components**: shadcn/ui (base-nova style, Base UI primitives)
- **Authentication**: Clerk (with Organizations/Billing support)
- **Error Tracking**: Sentry
- **Charts**: Recharts
- **Containerization**: Docker (Node.js & Bun Dockerfiles)
- **Package Manager**: Bun (preferred) or npm

The project follows a feature-based folder structure designed for scalability in SaaS applications, internal tools, and admin panels.

---

## Technology Stack Details

### Core Framework & Runtime

- Next.js 16.4 with App Router
- React 19.3
- TypeScript 7 (native compiler) with strict mode enabled

### Styling & UI

- Tailwind CSS v4 (using `@import 'tailwindcss'` syntax)
- PostCSS with `@tailwindcss/postcss` plugin
- shadcn/ui component library (Base UI primitives)
- CSS custom properties for theming (OKLCH color format)

### State Management

- Zustand 5.x for local UI state in the stateful demo features
- Nuqs for URL search params state management
- TanStack Form + Zod for form handling (`createFormHook` + shadcn `Field`-anatomy components)

### Data Fetching & Caching

- TanStack React Query for data fetching, caching, and mutations
- Server-side prefetching with `HydrationBoundary` + `dehydrate`
- Client-side `useQuery` + nuqs `shallow: true` for tables (no RSC round-trips on pagination/filter)
- `useMutation` + `invalidateQueries` for form submissions
- Query client singleton in `src/lib/query-client.ts`

### Authentication & Authorization

- Clerk for authentication and user management
- Clerk Organizations for multi-tenant workspaces
- Clerk Billing for subscription management (B2B)
- Client-side RBAC for navigation visibility

### Data & APIs

- TanStack Table for data tables
- TanStack React Query for data fetching and mutations
- Recharts for analytics/charts
- Service layer per feature (`api/types.ts` โ†’ `api/service.ts` โ†’ `api/queries.ts`)
- Route handlers at `src/app/api/` (for Route Handler or BFF patterns)
- Mock data in `src/constants/mock-api*.ts` (default, swap via service layer)
- API client utility in `src/lib/api-client.ts` (for fetch-based patterns)

### Development Tools

- ESLint 8.x with Next.js core-web-vitals config
- Prettier 3.x with prettier-plugin-tailwindcss
- Husky for git hooks
- lint-staged for pre-commit formatting

---

## Project Structure

```
/src
โ”œโ”€โ”€ app/                    # Next.js App Router
โ”‚   โ”œโ”€โ”€ auth/              # Authentication routes (sign-in, sign-up)
โ”‚   โ”œโ”€โ”€ dashboard/         # Dashboard routes
โ”‚   โ”‚   โ”œโ”€โ”€ overview/      # Parallel routes (@area_stats, @bar_stats, etc.)
โ”‚   โ”‚   โ”œโ”€โ”€ product/       # Product management pages
โ”‚   โ”‚   โ”œโ”€โ”€ kanban/        # Kanban board page
โ”‚   โ”‚   โ”œโ”€โ”€ chat/          # Messaging page
โ”‚   โ”‚   โ”œโ”€โ”€ ai-chat/       # AI chat streaming demo
โ”‚   โ”‚   โ”œโ”€โ”€ notifications/ # Notifications page
โ”‚   โ”‚   โ”œโ”€โ”€ workspaces/    # Organization management
โ”‚   โ”‚   โ”œโ”€โ”€ billing/       # Subscription billing
โ”‚   โ”‚   โ”œโ”€โ”€ exclusive/     # Pro plan feature example
โ”‚   โ”‚   โ””โ”€โ”€ profile/       # User profile
โ”‚   โ”œโ”€โ”€ api/               # API routes (if any)
โ”‚   โ”œโ”€โ”€ layout.tsx         # Root layout with providers
โ”‚   โ”œโ”€โ”€ page.tsx           # Landing page
โ”‚   โ”œโ”€โ”€ global-error.tsx   # Global error boundary
โ”‚   โ””โ”€โ”€ not-found.tsx      # 404 page
โ”‚
โ”œโ”€โ”€ components/
โ”‚   โ”œโ”€โ”€ ui/                # shadcn/ui components (50+ components)
โ”‚   โ”œโ”€โ”€ layout/            # Layout components (sidebar, header, etc.)
โ”‚   โ”œโ”€โ”€ forms/             # Field components (shadcn TanStack Form anatomy) + demos
โ”‚   โ”œโ”€โ”€ themes/            # Theme system components
โ”‚   โ”œโ”€โ”€ kbar/              # Command+K search bar
โ”‚   โ”œโ”€โ”€ icons.tsx          # Icon registry
โ”‚   โ””โ”€โ”€ ...
โ”‚
โ”œโ”€โ”€ features/              # Feature-based modules
โ”‚   โ”œโ”€โ”€ auth/              # Authentication components
โ”‚   โ”œโ”€โ”€ overview/          # Dashboard analytics
โ”‚   โ”œโ”€โ”€ products/          # Product management (React Query + nuqs)
โ”‚   โ”‚   โ”œโ”€โ”€ api/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ types.ts   # Type contract (response shapes, filters, payloads)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ service.ts # Data access layer (swap for your backend)
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ queries.ts # React Query options + key factories
โ”‚   โ”‚   โ”œโ”€โ”€ components/    # Listing, form, table components
โ”‚   โ”‚   โ”œโ”€โ”€ schemas/       # Zod schemas
โ”‚   โ”‚   โ””โ”€โ”€ constants/     # Filter options
โ”‚   โ”œโ”€โ”€ users/             # User management (React Query + nuqs)
โ”‚   โ”‚   โ”œโ”€โ”€ api/           # Same pattern: types.ts โ†’ service.ts โ†’ queries.ts
โ”‚   โ”‚   โ””โ”€โ”€ components/    # Listing, table components
โ”‚   โ”œโ”€โ”€ react-query-demo/  # React Query showcase (Pokemon API)
โ”‚   โ”œโ”€โ”€ kanban/            # Kanban board with dnd-kit
โ”‚   โ”œโ”€โ”€ chat/              # Messaging UI (conversations, bubbles, composer)
โ”‚   โ”œโ”€โ”€ ai-chat/           # Scripted useChat streaming demo
โ”‚   โ”œโ”€โ”€ notifications/     # Notification center & store
โ”‚   โ””โ”€โ”€ profile/           # Profile management
โ”‚
โ”œโ”€โ”€ config/                # Configuration files
โ”‚   โ”œโ”€โ”€ nav-config.ts      # Navigation with RBAC
โ”‚   โ””โ”€โ”€ ...
โ”‚
โ”œโ”€โ”€ hooks/                 # Custom React hooks
โ”‚   โ”œโ”€โ”€ use-nav.ts         # RBAC navigation filtering
โ”‚   โ”œโ”€โ”€ use-data-table.ts  # Data table state
โ”‚   โ””โ”€โ”€ ...
โ”‚
โ”œโ”€โ”€ lib/                   # Utility functions
โ”‚   โ”œโ”€โ”€ utils.ts           # cn() and formatters
โ”‚   โ”œโ”€โ”€ searchparams.ts    # Search param utilities
โ”‚   โ””โ”€โ”€ ...
โ”‚
โ”œโ”€โ”€ types/                 # TypeScript type definitions
โ”‚   โ””โ”€โ”€ index.ts           # Core types (NavItem, etc.)
โ”‚
โ””โ”€โ”€ styles/                # Global styles
    โ”œโ”€โ”€ globals.css        # Tailwind imports + view transitions
    โ”œโ”€โ”€ theme.css          # Theme imports
    โ””โ”€โ”€ themes/            # Individual theme files

/docs                      # Documentation
โ”‚   โ”œโ”€โ”€ clerk_setup.md     # Clerk configuration guide
โ”‚   โ”œโ”€โ”€ nav-rbac.md        # Navigation RBAC documentation
โ”‚   โ””โ”€โ”€ themes.md          # Theme customization guide

/scripts                   # Dev tooling
    โ”œโ”€โ”€ cleanup.js         # Feature removal, run via `bun run cleanup` (templates in cleanup-templates/, typechecked)
    โ””โ”€โ”€ cleanup-templates/ # Replacement files cleanup.js copies into the repo

Dockerfile                 # Node.js production Dockerfile
Dockerfile.bun             # Bun production Dockerfile
.dockerignore              # Docker build exclusions
```

---

## Build & Development Commands

```bash
# Install dependencies
bun install

# Development server
bun run dev          # Starts at http://localhost:3000

# Build for production
bun run build

# Start production server
bun run start

# Linting
bun run lint         # Run ESLint
bun run lint:fix     # Fix ESLint issues and format
bun run lint:strict  # Zero warnings tolerance

bun run typecheck    # tsc --noEmit

# Formatting
bun run format       # Format with Prettier
bun run format:check # Check formatting

# Git hooks
bun run prepare      # Install Husky hooks
```

---

## Environment Configuration

Copy `env.example.txt` to `.env.local` and configure:

### Required for Authentication (Clerk)

```env
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_...
CLERK_SECRET_KEY=sk_...

# Redirect URLs
NEXT_PUBLIC_CLERK_SIGN_IN_URL="/auth/sign-in"
NEXT_PUBLIC_CLERK_SIGN_UP_URL="/auth/sign-up"
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL="/dashboard/overview"
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL="/dashboard/overview"
```

### Optional for Error Tracking (Sentry)

```env
NEXT_PUBLIC_SENTRY_DSN=https://[email protected]/...
NEXT_PUBLIC_SENTRY_ORG=your-org
NEXT_PUBLIC_SENTRY_PROJECT=your-project
SENTRY_AUTH_TOKEN=sntrys_...
NEXT_PUBLIC_SENTRY_DISABLED="false"  # Set to "true" to disable in dev
```

**Note**: Clerk supports "keyless mode" โ€” run `npx clerk@latest init` to provision a development instance in seconds (no account needed; keys are written to `.env.local`).

---

## Code Style Guidelines

### TypeScript

- Strict mode enabled
- Use explicit return types for public functions
- Prefer interface over type for object definitions
- Use `@/*` alias for imports from src

### Formatting (Prettier)

```json
{
  "singleQuote": true,
  "jsxSingleQuote": true,
  "semi": true,
  "trailingComma": "none",
  "tabWidth": 2,
  "arrowParens": "always"
}
```

### ESLint Rules

- `@typescript-eslint/no-unused-vars`: warn
- `no-console`: warn
- `react-hooks/exhaustive-deps`: warn
- `import/no-unresolved`: off (handled by TypeScript)

### Component Conventions

- Use function declarations for components: `function ComponentName() {}`
- Props interface named `{ComponentName}Props`
- shadcn/ui components use `cn()` utility for class merging
- Server components by default, `'use client'` only when needed

---

## Theming System

The project uses a sophisticated multi-theme system with 10 built-in themes:

- `vercel` (default)
- `claude`
- `discord`
- `supabase`
- `mono`
- `notebook`
- `light-green`
- `zen`
- `astro-vista`
- `whatsapp`

### Theme Files

- CSS files: `src/styles/themes/{theme-name}.css`
- Theme registry: `src/components/themes/theme.config.ts`
- Font config: `src/components/themes/font.config.ts`
- Active theme provider: `src/components/themes/active-theme.tsx`

### Adding a New Theme

1. Create `src/styles/themes/your-theme.css` with `[data-theme='your-theme']` selector
2. Import in `src/styles/theme.css`
3. Add to `THEMES` array in `src/components/themes/theme.config.ts`
4. (Optional) Add fonts in `font.config.ts`
5. (Optional) Set as default in `theme.config.ts`

See `docs/themes.md` for detailed theming guide.

---

## Navigation & RBAC System

### Navigation Configuration

Navigation is organized into groups in `src/config/nav-config.ts`:

```typescript
import { NavGroup } from '@/types';

export const navGroups: NavGroup[] = [
  {
    label: 'Overview',
    items: [
      {
        title: 'Dashboard',
        url: '/dashboard/overview',
        icon: 'dashboard',
        shortcut: ['d', 'd'],
        items: [],
        access: { requireOrg: true } // RBAC check
      }
    ]
  }
];
```

### Access Control Properties

- `requireOrg: boolean` - Requires active organization
- `permission: string` - Requires specific permission
- `role: string` - Requires specific role
- `plan: string` - Requires specific subscription plan
- `feature: string` - Requires specific feature

### Client-Side Filtering

The `useFilteredNavItems()` hook in `src/hooks/use-nav.ts` filters navigation client-side using Clerk's `useOrganization()` and `useUser()` hooks. This is for UX only - actual security checks must happen server-side.

---

## Authentication Patterns

### Protected Routes

Route protection is resource-based (Clerk's recommended pattern): `src/proxy.ts` runs a bare `clerkMiddleware()` to attach the auth context, and every dashboard `layout`/`page` and API route handler performs its own check.

```tsx
import { auth } from '@clerk/nextjs/server';

export default async function Page() {
  await auth.protect(); // redirects signed-out users to sign-in
  // ...
}
```

API route handlers return 401 instead of redirecting:

```tsx
const { userId } = await auth();
if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
```

Pages that require an organization:

```tsx
import { auth } from '@clerk/nextjs/server';
import { redirect } from 'next/navigation';

export default async function Page() {
  await auth.protect();
  const { orgId } = await auth();
  if (!orgId) redirect('/dashboard/workspaces');
  // ...
}
```

### Plan/Feature Protection

Use Clerk's `<Show>` component for client-side:

```tsx
import { Show } from '@clerk/nextjs';

<Show when={{ plan: 'pro' }} fallback={<UpgradePrompt />}>
  <PremiumContent />
</Show>;
```

Use `has()` function for server-side checks:

```tsx
import { auth } from '@clerk/nextjs/server';

const { has } = await auth();
const hasFeature = has({ feature: 'premium_access' });
```

---

## Data Fetching Patterns

### Service Layer Architecture

Each feature has a three-file API layer:

```
src/features/<name>/api/
  types.ts      โ† Type contract (response shapes, filters, payloads)
  service.ts    โ† Data access functions (the ONE file to swap for your backend)
  queries.ts    โ† React Query options + query key factories (stable, never changes)
```

**`service.ts` is the only file you modify when connecting to a real backend.** Queries and components import from it โ€” they never change.

#### Backend Patterns

| Pattern                                            | How to implement                                                                            |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| **Server Actions + ORM** (Prisma/Drizzle/Supabase) | Add `'use server'` at top of `service.ts`, call ORM directly                                |
| **Route Handlers + ORM**                           | `service.ts` calls `/api/` routes via `apiClient`, route handlers call ORM                  |
| **BFF** (Next.js proxies to Laravel/Go/etc.)       | `service.ts` calls `/api/` routes via `apiClient`, route handlers proxy to external backend |
| **Direct external API** (frontend-only)            | `service.ts` calls external URL via `fetch()`                                               |
| **Mock** (default)                                 | `service.ts` calls in-memory fake data stores                                               |

Route handlers at `src/app/api/` are ready for patterns 2 and 3. `src/lib/api-client.ts` provides a typed `fetch` wrapper.

### Query Key Factories

Each feature defines a key factory in `queries.ts` for type-safe, hierarchical cache invalidation:

```tsx
export const entityKeys = {
  all: ['entities'] as const,
  list: (filters: EntityFilters) => [...entityKeys.all, 'list', filters] as const,
  detail: (id: number) => [...entityKeys.all, 'detail', id] as const
};

// Usage in queryOptions
queryKey: entityKeys.list(filters);

// Usage in mutations โ€” invalidate all entity queries
queryClient.invalidateQueries({ queryKey: entityKeys.all });
```

### React Query (Default for all new pages)

The project uses TanStack React Query with server-side prefetching and client-side cache management:

1. **Query options** defined in `queries.ts` โ€” shared between server prefetch and client hooks
2. **Server prefetch** using `void queryClient.prefetchQuery()` + `HydrationBoundary` + `dehydrate` โ€” `void` (fire-and-forget) is the standard TanStack pattern for Next.js App Router
3. **Client fetch** using `useSuspenseQuery()` โ€” integrates with React Suspense so prefetched data streams in without showing a loading skeleton on first load
4. **Suspense boundary** wraps the client component โ€” shows a fallback skeleton only on subsequent client-side navigations when cache is empty

```tsx
// Server component: prefetch + dehydrate
const queryClient = getQueryClient();
void queryClient.prefetchQuery(entitiesQueryOptions(filters)); // void, not await

return (
  <HydrationBoundary state={dehydrate(queryClient)}>
    <Suspense fallback={<Skeleton />}>
      <EntityTable />
    </Suspense>
  </HydrationBoundary>
);

// Client component: useSuspenseQuery (not useQuery)
const { data } = useSuspenseQuery(entitiesQueryOptions(filters));
```

**Why `void` + `useSuspenseQuery`:**

- `void` fires the prefetch without blocking the server component
- `useSuspenseQuery` integrates with React Suspense โ€” the pending query streams in via Next.js streaming SSR
- With `<Suspense fallback={<Skeleton />}>`: skeleton shows immediately while data streams in โ€” this is expected behavior, the skeleton IS the Suspense fallback during streaming
- Without `<Suspense>` wrapper: no skeleton, but the previous page stays visible until data fully resolves (feels like a slow navigation)
- Once data is cached (within `staleTime`), subsequent visits are instant โ€” no skeleton

**Why NOT `useQuery`:**

- `useQuery` doesn't integrate with Suspense โ€” returns `isLoading: true` and you must handle loading state manually
- Hydrated pending queries from `void` prefetch won't prevent the loading state
- Results in skeleton flash even when data is prefetched

### Mutations

Components import service functions for mutations. Use query key factories for invalidation:

```tsx
import { createEntity } from '../api/service';
import { entityKeys } from '../api/queries';

const mutation = useMutation({
  mutationFn: (data) => createEntity(data),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: entityKeys.all });
    toast.success('Created');
  }
});
```

### URL State Management

Use `nuqs` for search params state:

- `searchParamsCache` (server) โ€” reads params in server components
- `useQueryState` (client) โ€” reads/writes params in client components with `shallow: true`

### Data Tables

Tables use TanStack Table v9 (DiceUI data table) with React Query:

- Query options in `features/*/api/queries.ts`
- Column definitions in `features/*/components/*-tables/columns.tsx`, typed `ColumnDef<DataTableFeatures, T>[]`
- Table component in `src/components/data-table/data-table.tsx`
- Feature set + `DataTableFeatures` type in `src/lib/data-table-features.ts`; URL parsers in `src/lib/data-table-parsers.ts`
- Column pinning via `initialState.columnPinning` (`{ start: [], end: ['actions'] }`) in `useDataTable`

---

## Error Handling & Monitoring

### Sentry Integration

Sentry is configured for both client and server:

- Client config: `src/instrumentation-client.ts`
- Server config: `src/instrumentation.ts`
- Global error: `src/app/global-error.tsx`

To disable Sentry in development:

```env
NEXT_PUBLIC_SENTRY_DISABLED="true"
```

### Error Boundaries

- `global-error.tsx` - Catches all errors, reports to Sentry
- Parallel route `error.tsx` files for specific sections

---

## Testing Strategy

**Note**: This project does not include a test suite by default. Consider adding:

- **Unit tests**: Vitest or Jest for utilities and hooks
- **Component tests**: React Testing Library for UI components
- **E2E tests**: Playwright for critical user flows

Recommended test locations:

```
/src
  /__tests__           # Unit tests
  /features/*/tests    # Feature tests
/e2e                   # Playwright tests
```

---

## Deployment

Canonical guide: [docs/deployment.md](./docs/deployment.md) (Vercel, production environment variables, Docker).

### Vercel (Recommended)

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

### 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

### Docker

Production-ready Dockerfiles are included:

- `Dockerfile` โ€” Node.js-based
- `Dockerfile.bun` โ€” Bun-based

Both use `output: 'standalone'` in `next.config.ts`. Pass `NEXT_PUBLIC_*` vars as `--build-arg` at build time, and runtime secrets via `-e` at run time.

### Build Considerations

- Output: `standalone` (optimized for Docker/self-hosting)
- Images: Configured for `api.slingacademy.com`, `img.clerk.com`, `clerk.com`
- Sentry source maps uploaded automatically in CI

---

## Feature Cleanup System

A single `scripts/cleanup.js` file handles removal of optional features:

```bash
# Interactive mode โ€” prompts for each feature
node scripts/cleanup.js --interactive

# Remove specific features
node scripts/cleanup.js clerk           # Remove auth/org/billing
node scripts/cleanup.js kanban          # Remove kanban board
node scripts/cleanup.js chat            # Remove messaging UI
node scripts/cleanup.js ai-chat         # Remove AI chat demo
node scripts/cleanup.js notifications   # Remove notification center
node scripts/cleanup.js themes          # Keep one theme, remove rest
node scripts/cleanup.js sentry          # Remove error tracking

# Remove multiple at once
node scripts/cleanup.js kanban chat notifications

# Preview without changing files
node scripts/cleanup.js --dry-run kanban

# List all features
node scripts/cleanup.js --list
```

**Safety**: Script requires git repository with at least one commit. Use `--force` to skip.

Replacement files live in `scripts/cleanup-templates/` as real `.ts`/`.tsx` files typechecked by `tsc` and `next build`, so template rot fails loudly instead of shipping broken code.

After cleanup, delete `scripts/cleanup.js` and `scripts/cleanup-templates/` โ€” the dev server message auto-cleans on next start.

---

## Icon System

**All icons come from a single source: `src/components/icons.tsx`.**

The project uses `@tabler/icons-react` as the sole icon package. Every icon is re-exported through a centralized `Icons` object โ€” **never import directly from `@tabler/icons-react` or any other icon package**.

### Usage

```tsx
import { Icons } from '@/components/icons';

// In JSX
<Icons.search className='h-4 w-4' />
<Icons.chevronRight className='h-4 w-4' />

// Passing as a prop
icon={Icons.check}
```

### Adding a New Icon

1. Import the tabler icon in `src/components/icons.tsx`
2. Add a semantic key to the `Icons` object
3. Use `Icons.yourKey` everywhere โ€” never the raw import

```tsx
// In src/components/icons.tsx
import { IconNewIcon } from '@tabler/icons-react';

export const Icons = {
  // ...existing icons
  newIcon: IconNewIcon
};
```

### Available Icon Categories

| Category        | Example Keys                                                                  |
| --------------- | ----------------------------------------------------------------------------- |
| General         | `check`, `close`, `search`, `settings`, `trash`, `spinner`, `info`, `warning` |
| Navigation      | `chevronDown`, `chevronLeft`, `chevronRight`, `chevronUp`, `chevronsUpDown`   |
| Layout          | `dashboard`, `kanban`, `panelLeft`                                            |
| User            | `user`, `account`, `profile`, `teams`                                         |
| Communication   | `chat`, `notification`, `phone`, `video`, `send`                              |
| Files           | `page`, `post`, `media`, `fileTypePdf`, `fileTypeDoc`                         |
| Actions         | `add`, `edit`, `upload`, `share`, `login`, `logout`                           |
| Theme           | `sun`, `moon`, `brightness`, `laptop`, `palette`                              |
| Text formatting | `bold`, `italic`, `underline`, `text`                                         |
| Data / Charts   | `trendingUp`, `trendingDown`, `eyeOff`, `adjustments`                         |

### Icon Showcase Page

Browse all available icons at `/dashboard/elements/icons` โ€” a searchable grid of every icon in the registry.

### Why This Pattern?

- **Single source of truth** โ€” swap icon packages by editing one file
- **Semantic naming** โ€” `Icons.trash` is clearer than `IconTrash` scattered across files
- **Discoverability** โ€” autocomplete on `Icons.` shows every available icon
- **No direct dependencies** โ€” components never couple to a specific icon package

---

## Common Development Tasks

### Adding a New Feature (End-to-End)

1. Create `src/features/<name>/api/types.ts` โ€” response types, filter types, mutation payloads
2. Create `src/features/<name>/api/service.ts` โ€” data access functions (mock by default)
3. Create `src/features/<name>/api/queries.ts` โ€” query key factory + `queryOptions`
4. Create page route: `src/app/dashboard/<name>/page.tsx`
5. Create feature components in `src/features/<name>/components/`
6. Add navigation item in `src/config/nav-config.ts`
7. (Optional) Add route handlers in `src/app/api/<name>/` for REST API patterns
8. (Optional) Register new icon in `src/components/icons.tsx`

### Adding a New API Route

1. Create: `src/app/api/my-route/route.ts`
2. Export HTTP method handlers: `GET`, `POST`, etc.
3. For BFF pattern: proxy requests to your external backend

### Adding a shadcn Component

```bash
npx shadcn add component-name
```

### Adding a New Theme

See "Theming System" section above or `docs/themes.md`.

---

## Troubleshooting

### Common Issues

**Build fails with Tailwind errors**

- Ensure using Tailwind CSS v4 syntax (`@import 'tailwindcss'`)
- Check `postcss.config.js` uses `@tailwindcss/postcss`

**Clerk keyless mode popup**

- Run `npx clerk@latest init` to provision a dev instance in seconds (no account needed)
- It writes keys to `.env.local`; later you can claim application or set env variables

**Theme not applying**

- Check theme name matches in CSS `[data-theme]` and `theme.config.ts`
- Verify theme CSS is imported in `theme.css`

**Navigation items not showing**

- Check `access` property in nav config
- Verify user has required org/permission/role

---

## External Documentation

- [Next.js App Router](https://nextjs.org/docs/app)
- [Clerk Next.js SDK](https://clerk.com/docs/references/nextjs)
- [shadcn/ui](https://ui.shadcn.com/docs)
- [Tailwind CSS v4](https://tailwindcss.com/docs)
- [TanStack Table](https://tanstack.com/table/latest)
- [Sentry Next.js](https://docs.sentry.io/platforms/javascript/guides/nextjs/)

---

## Notes for AI Agents

1. **Always use `cn()` for className merging** - never concatenate strings manually
2. **Respect the feature-based structure** - put new feature code in `src/features/`
3. **Server components by default** - only add `'use client'` when using browser APIs or React hooks
4. **Type safety first** - avoid `any`, prefer explicit types
5. **Follow existing patterns** - look at similar components before creating new ones
6. **Environment variables** - prefix with `NEXT_PUBLIC_` for client-side access
7. **shadcn components** - don't modify files in `src/components/ui/` directly; extend them instead
8. **Icons** - NEVER import icons directly from `@tabler/icons-react` or any other icon package. All icons must be registered in `src/components/icons.tsx` and imported as `import { Icons } from '@/components/icons'`. To add a new icon: add the tabler import to `icons.tsx`, add a semantic key to the `Icons` object, then use `Icons.keyName` in your component.
9. **Page headers** - Always use `PageContainer` props (`pageTitle`, `pageDescription`, `pageHeaderAction`) for page headers. Never import `<Heading>` manually in pages โ€” `PageContainer` handles that internally.
10. **Forms** - Use `useAppForm` from `@/lib/form` with `form.AppField` rendering the shared field components (`field.TextField`, `field.SelectField`, โ€ฆ) from `@/components/forms/fields`. Each component follows the official shadcn TanStack Form anatomy; drop down to raw `form.Field` render props for one-off custom fields. Never use `useState` inside a render prop โ€” extract stateful controls into components.
11. **Button loading** - Use `<Button isLoading={isPending}>` for loading states. Uses CSS Grid overlap trick for zero layout shift. When `isLoading` is not passed, button behaves as default shadcn. `SubmitButton` in forms handles this automatically via form `isSubmitting` state.
12. **Data layer** - Always go through the service layer: `types.ts` โ†’ `service.ts` โ†’ `queries.ts`. Components import types from `types.ts`, functions from `service.ts`, query options from `queries.ts`. Never import from `@/constants/mock-api*` directly in components.