React Code Organization
Overview
As our application grows, maintainability becomes more important than writing code quickly.
Every file should have a single responsibility. Components should focus on rendering UI, while business logic, validation, schemas, and API calls should be separated into their own modules.
Core Principles
- Keep files small.
- One responsibility per file.
- Separate UI from business logic.
- Separate schemas from validation.
- Separate API calls from components.
- Prefer composition over large components.
- Build reusable components whenever possible.
File Size Guidelines
| File Size | Recommendation |
|---|---|
| Less than 300 lines | ✅ Ideal |
| 300 to 500 lines | ⚠️ Consider splitting |
| More than 500 lines | ❌ Refactor required |
| More than 1000 lines | 🚨 Not acceptable |
Single Responsibility Principle
Each file should have one purpose.
❌ Bad
One file contains:
- UI
- API calls
- Validation
- Form schema
- Business logic
- Utility functions
✅ Good
Separate them into:
- Component
- Schema
- Validation
- Service
- Hook
Component Guidelines
A React component should only be responsible for rendering UI and handling local interactions.
A component should:
- Render the interface
- Accept props
- Manage local state
- Call hooks
A component should not:
- Perform API requests directly
- Contain validation logic
- Define large form schemas
- Contain unrelated business logic
Schema Organization
Never place every schema inside one file.
❌ Bad
candidate-schema.ts
Basic Information
Experience
Projects
Skills
Education
Languages
Achievements
Large schema files quickly become difficult to maintain.
✅ Good
schemas/
basic-information.ts
experience.ts
projects.ts
skills.ts
education.ts
languages.ts
achievements.ts
index.ts
Example:
export const basicInformationSchema = [
{
name: "firstName",
type: "text",
label: "First Name",
},
];
API Layer
React components should never communicate directly with the backend.
❌ Bad
await axios.post("/candidate", data);
✅ Good
// candidate.service.ts
export async function updateCandidate(data) {
return api.patch("/candidate", data);
}
Inside the component:
await updateCandidate(formData);
Custom Hooks
Business logic belongs inside hooks.
Example:
hooks/
useCandidate.ts
useProjects.ts
useProfile.ts
Hooks should contain:
- Data fetching
- Mutations
- Derived state
- Complex business logic
Components should remain focused on rendering.
Reusable Field Definitions
Avoid repeating field definitions.
Instead of:
{
type: "text",
name: "firstName",
label: "First Name"
}
Create reusable helpers.
textField("firstName", "First Name");
Benefits:
- Less duplication
- Easier maintenance
- Consistent field definitions
Naming Conventions
Components
CandidateProfile.tsx
ExperienceSection.tsx
ProjectCard.tsx
Use PascalCase.
Hooks
useCandidate.ts
useProjects.ts
Always start hook names with use.
Services
candidate.service.ts
auth.service.ts
Schemas
basic-information.ts
experience.ts
projects.ts
Use kebab-case.
Types
candidate.types.ts
project.types.ts
Component Size
If a component is responsible for:
- Rendering UI
- Fetching data
- Validation
- API calls
- Business logic
- State management
It should be split into multiple files.
Recommended Limits
| Item | Recommendation |
|---|---|
| Component | Around 300 lines or less |
| Hook | Around 200 lines or less |
| Service | Around 200 lines or less |
| Schema | One section per file |
| Validation | One section per file |
Example
Instead of:
CandidateEdit.tsx
1200 lines
Prefer:
CandidateEdit.tsx
components/
BasicInformationSection.tsx
ExperienceSection.tsx
SkillsSection.tsx
ProjectsSection.tsx
schemas/
validation/
hooks/
services/
Each file should have one clear responsibility.
Benefits
Following these guidelines provides several advantages:
- Easier code reviews
- Fewer merge conflicts
- Better readability
- Better testability
- Faster onboarding for new developers
- Easier feature development
- More reusable code
Summary
- Keep files focused.
- Keep components small.
- One responsibility per file.
- Separate UI from business logic.
- Separate schemas from validation.
- Keep API calls inside services.
- Keep business logic inside hooks.
- Reuse components and helper functions whenever possible.