Naming Conventions
Consistent naming makes the codebase navigable at a glance. Every file, function, type, and constant follows a predictable pattern.
Files and Directories
| Item | Convention | Example |
|---|---|---|
| Component directory | PascalCase | src/common/components/Button/ |
| Component file | PascalCase.tsx | Button.tsx |
| Component styles | PascalCase.styles.ts | Button.styles.ts |
| Component types | PascalCase.types.ts | Button.types.ts |
| Component barrel | lowercase | index.ts |
| Hook file | camelCase, starts with use | useBottomPadding.ts |
| Store file | camelCase, ends with Store | authStore.ts |
| Service file | camelCase, ends with Service | authService.ts |
| Schema file | camelCase, ends with Schema | loginSchema.ts |
| Screen file (app/) | lowercase with hyphens | forgot-password.tsx |
| Layout file (app/) | underscore prefix | _layout.tsx |
| Constants file | camelCase | constants.ts |
| i18n locale files | lowercase | en.json, ar.json |
Components
Component names are always PascalCase. Props interfaces are named {ComponentName}Props.
// src/common/components/UserCard/UserCard.tsx
import type { UserCardProps } from './UserCard.types';
export function UserCard({ name, avatar }: UserCardProps) {
// ...
}// src/common/components/UserCard/UserCard.types.ts
export interface UserCardProps {
name: string;
avatar?: string;
}Export Rules
Named exports for everything in src/. Default exports only for screen files in app/.
// CORRECT -- named export in src/
export function Button({ title, onPress }: ButtonProps) { /* ... */ }
// CORRECT -- default export in app/ screen files
export default function HomeScreen() { /* ... */ }// WRONG -- default export from a component in src/
export default function Button() { /* ... */ }Expo Router requires default exports for screen files in the app/ directory. This is the only place where default exports are used.
Hooks
- Always start with
use:useAuthStore,useNetworkStatus,useBottomPadding - File names match the exported hook:
useNetworkStatus.ts - Return objects (not arrays), unless it is a classic two-element state tuple
// CORRECT -- returns a named object
export function useScreenDimensions() {
return { width, height, isLandscape };
}
// WRONG -- unnamed array from a custom hook
export function useScreenDimensions() {
return [width, height];
}Stores
Zustand stores are exported as hooks with the use prefix and the Store suffix.
| Item | Convention | Example |
|---|---|---|
| Store hook | use{Feature}Store | useAuthStore |
| State interface | {Feature}State | AuthState |
| File name | {feature}Store.ts | authStore.ts |
TypeScript Types and Interfaces
- Interfaces for object shapes:
interface ButtonProps - Type aliases for unions, primitives, and mapped types:
type ButtonVariant = 'primary' | 'secondary' - All type-only imports use the
typekeyword:
import type { ButtonProps } from './Button.types';
import type { User } from '@/types';No any types. Use unknown and narrow, or define a proper type.
Constants
| Kind | Convention | Example |
|---|---|---|
| True constants | SCREAMING_SNAKE_CASE | STORAGE_KEYS, AVATAR_SIZES |
| Config objects | camelCase | breakpoints, spacing |
Last updated on