Naming Conventions
Table of Contents
Folder Structure
Root Level Folders (Singular)
src/
├── modules/ // All custom modules
├── shared/ // Shared resources
├── routes/ // Root route
├── vendor/ // Third party components
Base Module Structure
src/
├── modules/
│ ├── auth/
│ │ ├── component/ // UI Components
│ │ ├── graphql/ // GraphQL operations
│ │ ├── route/ // Route configurations
│ │ ├── schema/ // Form validations
│ │ ├── service/ // API/Business logic
│ │ ├── hook/ // Custom hooks
│ │ ├── util/ // Helper functions
│ │ ├── constant/ // Constants, enums
│ │ ├── config/ // Module configuration
│ │ └── type/ // TypeScript types
│ │
│ ├── user/
│ │ ├── component/
│ │ ├── graphql/
│ │ ├── route/
│ │ ├── service/
│ │ ├── hook/
│ │ ├── util/
│ │ ├── constant/
│ │ ├── config/
│ │ └── type/
│ │
│ └── product/ // Similar structure for each module
│
└── shared/ // Shared across modules
├── component/ // Common components
├── util/ // Common utilities
├── hook/ // Shared hooks
├── constant/ // Global constants
├── service/ // Base services
└── type/ // Shared types
Directory Purposes
Each directory serves a specific purpose and should contain related code following these guidelines:
component/
Place here when code:
- Renders UI elements
- Handles component-level state
- Contains presentation logic
// UserList.tsx - Component for displaying users
// UserForm.tsx - Form component for user data
// UserCard.tsx - Card component for user details
service/
Place here when code:
- Makes API calls
- Handles complex business logic
- Manages data transformations
- Interacts with external services
// user.service.ts
export class UserService {
async fetchUsers() { ... } // API calls
async processUserData() { ... } // Data processing
validateUserPermissions() { ... }// Business logic
}
util/
Place here when code:
- Is pure function (same input → same output)
- Has no side effects
- Can be reused across components
// formatter.ts
export const formatCurrency = (amount: number) => { ... }
export const formatDate = (date: Date) => { ... }
helper/
Place here when code:
- Helps with specific module tasks
- Contains module-specific utility functions
- Is not pure enough for utils
// user.helper.ts
export const enrichUserData = (user: User) => { ... }
export const calculateUserMetrics = (data: UserData) => { ... }
hook/
Place here when code:
- Manages reusable stateful logic
- Handles side effects
- Provides shared behavior
// useUser.ts
export const useUser = (userId: string) => {
// State and effect management
};
constant/
Place here when defining:
- Unchanging values
- Enums
- Configuration constants
// user.constant.ts
export const USER_ROLES = {
ADMIN: "admin",
USER: "user",
} as const;
config/
Place here when code:
- Configures module behavior
- Sets up feature flags
- Defines module settings
// table.config.ts
export const userTableConfig = {
pageSize: 10,
sortableColumns: ["name", "email"],
};
type/
Place here when defining:
- TypeScript interfaces
- Type definitions
- Type guards
// user.type.ts
export interface User {
id: string;
name: string;
}
File Naming
File Type Extensions & Organization
Component Files (.tsx)
- Use PascalCase to match React component names
- Component name should match its primary exported component
UserList.tsx
ProductDetail.tsx
SidebarMenu.tsx
UserList.test.tsx // Test files
Utility Files (.ts)
- Use camelCase for utility functions
- Name should be descriptive of functionality
- Group related utilities in a single file
formatDate.ts
validateInput.ts
apiClient.ts
Type Files (.type.ts)
- Use camelCase with
.type.ts
extension - Separate types by domain/feature
- Include both interfaces and types for the domain
user.type.ts
product.type.ts
api.type.ts
Hook Files (.ts)
- Start with 'use' prefix (React convention)
- Use camelCase
- Name should describe hook's purpose
useAuth.ts
useWindowSize.ts
useDebounce.ts
Schema Files (kebab-case)
Use kebab-case for all file and folder names to ensure cross-platform compatibility and URL friendliness.
product-form.schema.ts
user-profile.schema.ts
auth-validation.schema.ts
GraphQL Files (.ts)
- Use singular form for directory and file names
- Keep GraphQL operations in respective module directories
- Use camelCase for GraphQL operation constants
module/
└── user/
└── graphql/
├── query/
│ ├── get-user.ts
│ └── get-profile.ts
├── mutation/
│ ├── update-user.ts
│ └── delete-user.ts
└── fragment/
└── user-field.ts
// graphql/query/get-user.ts
export const getUser = `
query GetUser($id: ID!) {
user(id: $id) {
...UserField
}
}
`
// graphql/fragment/user-field.ts
export const userField = `
fragment UserField on User {
id
name
email
}
`
// graphql/mutation/update-user.ts
export const updateUser = `
mutation UpdateUser($id: ID!, $input: UserInput!) {
updateUser(id: $id, input: $input) {
...UserField
}
}
`
Route Naming
URL Structure
- Always use plural form in URLs for resource collections
- Maintain plural form throughout the resource path hierarchy
// Correct URL Structure
/users // List users
/users/$userId // User details
/users/$userId/settings // User settings
/users/create // Create user
Route Variable Names
Route variable names should reflect the purpose of the route while following the URL structure:
// List route: /users
export const usersListRoute = createRoute({
path: "/users",
component: UsersList,
});
// Detail route: /users/$userId
export const userDetailRoute = createRoute({
path: "/users/$userId",
component: UserDetail,
});
// Settings route: /users/$userId/settings
export const userSettingsRoute = createRoute({
path: "/users/$userId/settings",
component: UserSettings,
});
// Create route: /users/create
export const userCreateRoute = createRoute({
path: "/users/create",
component: UserCreate,
});
// Combined routes export
export const userRoutes = privateLayoutRoute.addChildren([
usersListRoute,
userDetailRoute,
userSettingsRoute,
userCreateRoute,
]);
Combined Route Exports
- Always use plural (it's a collection)
export const userRoutes = privateLayoutRoute.addChildren([
usersListRoute,
userProfileRoute,
userSettingsRoute,
]);
URL Paths:
- Always plural (
/users
,/products
,/posts
) - Consistent through the entire path
- Use kebab-case for multi-word resources (
/blog-posts
)
Route Variable Names:
- Use camelCase
- Name reflects the route's purpose
- Format:
{resource}{Action}Route
- Examples:
usersListRoute
for listinguserDetailRoute
for detailsuserCreateRoute
for creationuserEditRoute
for editing
Examples for Different Resources
Posts
// URLs Route Names
/posts postsListRoute
/posts/$postId postDetailRoute
/posts/$postId/edit postEditRoute
/posts/create postCreateRoute
Products
// URLs Route Names
/products productsListRoute
/products/$productId productDetailRoute
/products/$productId/edit productEditRoute
/products/create productCreateRoute
Schema Organizations
Simple Forms (1-3 fields)
Keep schemas focused and single-purpose:
// schema/login-form.schema.ts
import { z } from "zod";
import { commonValidationSchema } from "@/lib/schemas/common-validation.schema";
export const loginFormSchema = z.object({
email: commonValidationSchema.email,
password: commonValidationSchema.password,
});
export type LoginFormData = z.infer<typeof loginFormSchema>;
Complex Forms (4+ fields)
Group related schemas with clear sections:
// schema/user-form.schema.ts
import { z } from "zod";
// Basic Information
export const userBasicSchema = z.object({
firstName: z.string().min(2),
lastName: z.string().min(2),
email: z.string().email(),
phone: z.string().optional(),
});
// Address Information
export const userAddressSchema = z.object({
street: z.string(),
city: z.string(),
state: z.string(),
zipCode: z.string(),
});
// Combined schema
export const userCompleteSchema = userBasicSchema.merge(userAddressSchema);
// Type exports
export type UserBasicData = z.infer<typeof userBasicSchema>;
export type UserAddressData = z.infer<typeof userAddressSchema>;
export type UserCompleteData = z.infer<typeof userCompleteSchema>;
Why Combined Schemas?
-
Full Form Validation
- Validate entire form data at once
- Ensure all required fields are present
- Maintain data consistency
-
Complete Type Safety
- Get TypeScript types for the entire form
- Ensure all data is properly typed
- Prevent missing fields
-
API Submissions
- Validate complete payload before submission
- Ensure data integrity
- Maintain consistent data structure
Best Practices
Do's
- Keep folder names singular
- Use descriptive, purpose-indicating names
- Maintain consistent casing per file type
- Group related files together
- Use appropriate file extensions (.ts, .tsx)
Don'ts
- Mix naming conventions within same type
- Use unclear abbreviations
- Create deeply nested folder structures
- Use special characters in names
- Mix plural and singular forms