CodeBelt Code Style Guide
Opinionated TypeScript and React code style for consistency, maintainability, and scalability. Apply these rules when writing new code, reviewing existing code, or refactoring.
Quick Reference
File Placement
| Code Type | Location |
|-----------|----------|
| Page component | components/pages/{pageName}/ |
| Reusable domain component | components/shared/{componentName}/ |
| Pure presentational primitive | components/ui/{componentName}/ |
| API logic | services/{provider}/{service}/ or services/{service}/ |
| Reusable function | utils/{utilName}/ |
| React hook | hooks/use{HookName}/ |
| Global state store | stores/{storeName}/ |
| Global stylesheet | styles/ |
| Next.js route files | app/ |
| Third-party config | libs/{libraryName}/ |
See COMPONENTS.md for the decision tree between ui/, shared/, and pages/.
File Extensions
| Extension | Purpose |
|-----------|---------|
| .ts{x} | Main code (.tsx only when file contains JSX) |
| .test.ts | Tests (placed next to the file being tested) |
| .types.ts | TypeScript types |
| .utils.ts{x} | Helper functions (components only — not in utils/ folders) |
| .utils.test.ts | Tests for component helper functions |
| .schemas.ts | Zod validation schemas |
| .schemas.test.ts | Tests for schemas |
| .dynamic.tsx | Next.js dynamic import wrapper (ssr: false) |
| .constants.ts{x} | Static objects and constants |
| .store.ts | State store (see STORES.md) |
| .module.css | Co-located CSS Module (see STYLING.md) |
Naming Conventions
| Context | Convention | Example |
|---------|------------|---------|
| Component folders | camelCase | userCard/ |
| Component files | PascalCase | UserCard.tsx |
| Everything else | camelCase | httpClient.ts |
| Variables/params | Descriptive (no single chars) | event not e |
| Constants | camelCase | maxRetries not MAX_RETRIES |
| State updater args | previous prefix | previousData, previousState |
| Internal handlers | handle prefix | handleSubmit, handleFilterChange |
| Callback props | on prefix | onSubmit, onFilterChange |
Exports
All files use named exports only:
export function Component() {}
No default exports. No barrel files in application code.
Exception — Next.js special files: the Next.js app router requires export default for these route-segment files, and only these:
app/**/layout.tsxapp/**/page.tsxapp/**/loading.tsxapp/**/error.tsxapp/**/not-found.tsxapp/**/template.tsxapp/**/default.tsxapp/**/route.ts
Everywhere else (components, services, hooks, utils, stores), named exports only.
Component Files
One component per .tsx file — no exceptions. Every component, no matter how small, gets its own subfolder and file.
Workflows
Choosing a Component Tier
Before creating a component, decide where it lives:
- Is it domain-agnostic (Button, Heading, Input, Modal)? →
components/ui/{componentName}/ - Is it route-specific (only used by one page)? → nest it inside the page folder:
components/pages/{pageName}/{componentName}/ - Was a page-local component just imported by a second page? → promote it to
components/shared/{componentName}/
Default is page-local first. Promote to shared/ only when a second consumer appears — premature promotion creates components that try to serve everyone and fit no one.
See COMPONENTS.md for the full hierarchy.
Creating a New Component
- Pick the tier (see above) and create folder:
components/{ui|shared|pages/{pageName}}/{componentName}/ - Create component file:
{ComponentName}.tsxwith Props type and JSX only - Extract types to
{ComponentName}.types.ts(if needed beyond Props) - Extract constants to
{ComponentName}.constants.ts(if any) - Extract helpers to
{ComponentName}.utils.ts(if any) - Create
{ComponentName}.dynamic.tsxif the component is client-only and needs lazy loading (see COMPONENTS.md) - Verify: one component per file, Props not exported, named export
Creating a New Page
- Create folder:
components/pages/{pageName}/ - Create
{PageName}.tsx(the page component itself) - If multiple nested children share types, create
{PageName}.types.tsand import down — never re-export types from a child.types.ts - If the page needs mappers or helpers shared across children, create
{PageName}.utils.ts - Nest child components in subfolders:
components/pages/{pageName}/{childName}/{Child}.tsx - If using Next.js app router, the route file in
app/imports and renders the page component (see Exports exception above)
Creating a New Service
- Create folder. Use
services/{provider}/{serviceName}/when the app talks to multiple backends; useservices/{serviceName}/when there is only one. See SERVICES.md. - Create main file:
{serviceName}Service.tswith fetch functions and query hooks. Add'use client'at the top when the file exports React Query hooks (see SERVICES.md). - Create schema file:
{serviceName}Service.schemas.tswith Zod schemas - Create constants file:
{serviceName}Service.constants.tswith query keys - Verify: schema and type share same name, comment with HTTP method and path
Creating a New Hook
- Create folder:
hooks/use{HookName}/ - Create hook file:
use{HookName}.ts - Create test file:
use{HookName}.test.ts
Creating a New Utility
- Create folder:
utils/{utilName}/ - Create main utility file:
{utilName}.ts— all helper functions go here (no.utils.tsfile) - Create constants file:
{utilName}.constants.ts(if any) - Create test file:
{utilName}.test.ts
Note: Unlike components, utility folders do not use
.utils.tsfiles. The main{utilName}.tsfile itself is the utility — adding a.utils.tsbeside it would be redundant.
Detailed References
- Component patterns, tiers, and hierarchy: See COMPONENTS.md
- Service and schema patterns: See SERVICES.md
- State stores: See STORES.md
- Styling (Tailwind + CSS Modules): See STYLING.md
- Testing patterns: See TESTING.md
- Common mistakes to avoid: See MISTAKES.md
Scan to join WeChat group