Skip to Content
Conventions & Standards

Conventions & Standards

Coding conventions, naming patterns, and standards used across the Numa monorepo.

File naming

ContextConventionExamples
Route filesNext.js conventionpage.tsx, layout.tsx, error.tsx, not-found.tsx
Componentskebab-casefilter-modal.tsx, new-client-modal.tsx
Page viewskebab-casepage-view.tsx
Hooks fileskebab-casehooks.ts, use-delayed-show.ts
Helper fileskebab-casefuncs.ts, form-diff.ts
Service fileskebab-caseclients.service.ts, auth.service.ts
Config fileskebab-caseeslint.config.mjs, postcss.config.mjs

File extensions

ExtensionUsage
.tsTypeScript files without JSX
.tsxTypeScript files with JSX (components)
.mjsESM config files (eslint, prettier, next)
.jsonData, translations, package configs
.cssStylesheets (globals, Tailwind theme)
.mdxDocumentation (Nextra content)

TypeScript naming

Types and interfaces

  • T prefix for exported types: TMeUser, TLocale, TPlatform, TErrorPageProps, TButtonProps
  • Keep types co-located with their domain or in @numa/config/types/

Components

  • G prefix for shared UI components: GButton, GInput, GText, GIcon, GForm, GTable
  • PascalCase for component names
  • Exported functions in .tsx files should use PascalCase (enforced by ESLint)

Hooks

  • use prefix: 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 modals

Shared components

  • Index barrels: shared/ui/button/index.tsx
  • Types alongside components or in @numa/config/types/

ESLint rules

Key rules enforced across the monorepo:

RuleSettingEffect
no-consoleerrorUse @numa/utils/logger instead
prettier/prettiererrorCode formatting enforced
@typescript-eslint/no-unused-varserrorNo unused variables (except _ prefix)
Exported .tsx funcsPascalCaseComponent naming enforced

Prettier config

SettingValue
SemicolonsYes
QuotesDouble
Print width100
Line endingsLF
Trailing commaAll

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