Skip to content

TypeScript

Use names a developer can guess before searching. Component, hook, schema, query-key, route, and collection names appear in Storybook, stack traces, docs, and code review, so treat public names as contracts.

Core conventions

ContextConventionExample
Components and typesPascalCaseUserCreateDialog, PaginatedResponse<T>
Hooksuse + PascalCaseuseCurrentUser
Functions and variablescamelCaseformatCurrency, currentUser
Zod schemascamelCase + SchemauserCreateSchema
Constants and env varsSCREAMING_SNAKE_CASEMAX_PAGE_SIZE, DATABASE_URL
Component, story, and test fileskebab-caseuser-create-dialog.test.tsx
npm packagesscoped kebab-case@dmwd-io/design-system
npm scriptskebab or colon namespacetest:unit

React and TanStack

ContextConventionExample
Component symbolPascalCaseUserCreateDialog
Component filekebab-caseuser-create-dialog.tsx
Event handler propon<Event>onSubmit
Event handler implementationhandle<Event>handleSubmit
Boolean propis, has, or should prefixisLoading
Context and provider<Name>Context, <Name>ProviderAuthContext, AuthProvider
Store hookuse<Domain>StoreuseCartStore

TanStack Query keys are arrays ordered broad to narrow. Keep one key factory per domain so invalidation is precise.

export const invoiceKeys = {
all: ["billing", "invoice"] as const,
lists: () => [...invoiceKeys.all, "list"] as const,
list: (filters: InvoiceFilters) => [...invoiceKeys.lists(), filters] as const,
detail: (id: string) => [...invoiceKeys.all, "detail", id] as const,
};

Astro

Astro filenames become routes and content contracts.

ContextConventionExample
Page routekebab-case under src/pages/src/pages/about-us.astro
Dynamic routebracket paramsrc/pages/invoices/[id].astro
Rest route[...slug].astrosrc/pages/docs/[...slug].astro
Layout file and symbolkebab-case file, PascalCase symbolbase-layout.astro, BaseLayout
Collectionplural noun, kebab-case foldersrc/content/blog-posts/
Entry slugkebab-case2026-06-launch.md

Collection keys appear in every getCollection() call. Treat a rename as a breaking change.

Do and do not

DoDo not
useCurrentUsercurrentUserHook
userCreateSchemaUserCreateZod
UserCreateDialog symboluserCreateDialog symbol
user-create-dialog.tsx fileUserCreateDialog.tsx file
handleSelect implementationonSelect implementation
invoiceKeys.detail(id)inline query-key arrays everywhere
src/pages/invoices/[id].astrosrc/pages/invoices/id.astro

Enforcement

Use TypeScript strictness, lint naming rules, and filename-case rules in CI. For repos on ESLint, @typescript-eslint/naming-convention and unicorn/filename-case cover most cases. For Biome repos, use the equivalent naming-convention rule.

See also