Coding Conventions
TypeScript, React, and file naming conventions. No any, no console.log, Zod at edges.
Coding Conventions
Purpose: Framework coding standards for new projects. Copy relevant sections into your project's
CLAUDE.mdand customize.
Core principles
1. Template-first rule
Before designing or building any UI, ask: "Why can't I use the templates we already bought?"
- Check Shadcnblocks Premium first — Only build custom if no suitable template exists or modification would exceed building fresh
- Gap documentation — See
standards/reference/premium-blocks.mdfor the full catalog and gap documentation process
2. Toasts via shadcn Sonner wrapper
- Always import
Toaster— From@/components/ui/sonner, never directly from"sonner" - No
richColors— It bypasses theme variables
import { Toaster } from "@/components/ui/sonner"
<Toaster position="bottom-right" />
3. localStorage key registry
Maintain a registry of all localStorage keys in the project's CLAUDE.md to prevent collisions across features.
Each entry documents the key, its purpose, and the feature that owns it.
## localStorage Keys
| Key | Purpose | Owner |
|-----|---------|-------|
| `theme` | Selected color theme | Theme system |
| `font-family` | Selected font | Appearance |
| `sidebar:state` | Sidebar open/collapsed | Admin scaffold |
- Add new keys to the registry — When introducing localStorage usage
- Check the registry first — Before choosing a key name
4. Server state vs client state
- TanStack Query — Data from server (API calls, cached data)
- Zustand — UI state (sidebar open, theme, local preferences)
Never put server data in Zustand. Never fetch with Zustand.
5. Prototype exception
date-fnsin scaffold — Required by shadcn/ui Calendar; acceptable because prototypes are never promoted to production- Production code — Must use Luxon per Locked Decisions
- Native
Dateis also prohibited in production
- Native
Code patterns
Parameterize, don't duplicate
When building responsive variants (mobile vs desktop), use a prop to control behavior -- don't create two near-identical components.
function LockMultiSelect({ compact = false }: { compact?: boolean }) {
// Single component, behavior branches on `compact`
}
Smart column hiding
When data quality varies by filter context, auto-hide columns with poor coverage rather than showing mostly-empty cells.
const actorCoverage = events.filter(e => e.actor).length / events.length;
const showActorColumn = actorCoverage >= 0.5; // 50% threshold
Scaffold extension pattern
When building features on the prototype scaffold, follow this directory structure to keep feature code separate from scaffold infrastructure:
| Directory | Purpose | Reset behavior |
|---|---|---|
components/ui/ |
shadcn/ui primitives (scaffold) | Preserved |
components/admin/ |
Admin scaffold components (sidebar, header, data table) | Preserved |
components/app/ |
Feature components built for this project | Cleared on reset |
lib/data/ |
Zod schemas + seed data (scaffold) | Preserved |
lib/ (other files) |
Feature business logic, stores, utilities | Cleared on reset |
app/(admin)/ |
Admin routes and layouts (scaffold) | Preserved |
app/ (other routes) |
Feature pages | Cleared on reset |
Extension rules
- Never modify scaffold files — Don't edit
components/admin/,components/ui/,lib/data/, orapp/(admin)/layout.tsxfor feature work - Place feature components in
components/app/— Reusable components built during feature development - Place feature logic in
lib/— Stores, utilities, and helpers outside oflib/data/ - Extend the admin layout via composition — Add feature-specific headers or navigation by rendering them inside the admin layout's
childrenslot, not by modifying the layout itself - Register shell components — If you define a layout component (e.g.,
AppHeader), verify it's actually rendered in the appropriate layout
Last updated: March 2026