Conventions & Standards
Coding conventions, naming patterns, and standards used across the Numa monorepo.
File naming
| Context | Convention | Examples |
|---|---|---|
| Route files | Next.js convention | page.tsx, layout.tsx, error.tsx, not-found.tsx |
| Components | kebab-case | filter-modal.tsx, new-client-modal.tsx |
| Page views | kebab-case | page-view.tsx |
| Hooks files | kebab-case | hooks.ts, use-delayed-show.ts |
| Helper files | kebab-case | funcs.ts, form-diff.ts |
| Service files | kebab-case | clients.service.ts, auth.service.ts |
| Config files | kebab-case | eslint.config.mjs, postcss.config.mjs |
File extensions
| Extension | Usage |
|---|---|
.ts | TypeScript files without JSX |
.tsx | TypeScript files with JSX (components) |
.mjs | ESM config files (eslint, prettier, next) |
.json | Data, translations, package configs |
.css | Stylesheets (globals, Tailwind theme) |
.mdx | Documentation (Nextra content) |
TypeScript naming
Types and interfaces
Tprefix for exported types:TMeUser,TLocale,TPlatform,TErrorPageProps,TButtonProps- Keep types co-located with their domain or in
@numa/config/types/
Components
Gprefix for shared UI components:GButton,GInput,GText,GIcon,GForm,GTable- PascalCase for component names
- Exported functions in
.tsxfiles should use PascalCase (enforced by ESLint)
Hooks
useprefix:useGetClientsQuery,useAppEssentials,useDelayedShow- Auto-generated hooks follow:
use+PascalCase(key)+Query/Mutation
Constants
- UPPER_SNAKE_CASE for constants:
PLATFORMS,VAR_ACCESS_TOKEN,CROSS_APP_URLS VAR_prefix for cookie/env variable names:VAR_ACCESS_TOKEN,VAR_REFRESH_TOKEN
Import conventions
In packages (packages/*)
Always use @numa/* workspace imports:
import { PLATFORMS } from "@numa/config/platforms";
import { cn } from "@numa/utils/cn";Never use @/ — it only works inside apps.
In apps (apps/*)
@/for app-local imports:import Header from "@/components/header"@numa/*for shared packages:import { GButton } from "@numa/ui/shared/ui/button"
Component structure
Route folders
feature/
├── page.tsx # Route entry (thin wrapper)
├── page-view.tsx # Main content (client component)
├── filter-modal.tsx # Filter UI (if list page)
├── funcs.ts # Column definitions, helpers
├── hooks.ts # Page-specific hooks
└── new-item-modal.tsx # Create/edit modalsShared components
- Index barrels:
shared/ui/button/index.tsx - Types alongside components or in
@numa/config/types/
ESLint rules
Key rules enforced across the monorepo:
| Rule | Setting | Effect |
|---|---|---|
no-console | error | Use @numa/utils/logger instead |
prettier/prettier | error | Code formatting enforced |
@typescript-eslint/no-unused-vars | error | No unused variables (except _ prefix) |
Exported .tsx funcs | PascalCase | Component naming enforced |
Prettier config
| Setting | Value |
|---|---|
| Semicolons | Yes |
| Quotes | Double |
| Print width | 100 |
| Line endings | LF |
| Trailing comma | All |
Git hooks
- Husky manages git hooks
- lint-staged runs ESLint and Prettier on staged files before commit
State management
- Server state: TanStack Query (no Redux, Zustand, or Jotai)
- Client state: React Context for auth, user essentials
- Form state: TanStack Form
- No global store — each concern has its own context or query cache
Last updated on