We use cookies to enhance your experience on the site
CodeWorlds

React project structure

A well-organized project structure is crucial for code maintainability, easy navigation, and scalability of React applications. In this module, we'll look at best practices for structuring React projects and understand how to organize files and components.

Basic React project structure

Regardless of whether you use Create React App, Vite, or another tool, the basic structure of a React project usually looks similar:

1my-react-project/
2β”œβ”€β”€ node_modules/        # Installed dependencies
3β”œβ”€β”€ public/              # Publicly accessible static files
4β”œβ”€β”€ src/                 # Application source code
5β”‚   β”œβ”€β”€ components/      # Reusable components
6β”‚   β”œβ”€β”€ pages/           # Components representing entire pages
7β”‚   β”œβ”€β”€ assets/          # Assets such as images, fonts, etc.
8β”‚   β”œβ”€β”€ utils/           # Utility functions
9β”‚   β”œβ”€β”€ services/        # Code responsible for API communication
10β”‚   β”œβ”€β”€ hooks/           # Custom React hooks
11β”‚   β”œβ”€β”€ context/         # React contexts
12β”‚   β”œβ”€β”€ App.js           # Main application component
13β”‚   └── index.js         # Application entry point
14β”œβ”€β”€ .gitignore           # Files ignored by Git
15β”œβ”€β”€ package.json         # Project info and dependencies
16└── README.md            # Project documentation

Let's discuss the most important directories and their purposes:

The src/ directory

The

src/
directory is the heart of your React application. It contains all the logic, components, and assets. Here's how this directory is typically organized:

components/

Contains reusable components that are used in various parts of the application. There are several approaches to structuring components:

Flat structure

1components/
2β”œβ”€β”€ Button.jsx
3β”œβ”€β”€ Button.css
4β”œβ”€β”€ Header.jsx
5β”œβ”€β”€ Header.css
6β”œβ”€β”€ Footer.jsx
7└── Footer.css

Grouped structure (preferred for larger projects)

1components/
2β”œβ”€β”€ Button/
3β”‚   β”œβ”€β”€ Button.jsx
4β”‚   β”œβ”€β”€ Button.css
5β”‚   └── index.js
6β”œβ”€β”€ Header/
7β”‚   β”œβ”€β”€ Header.jsx
8β”‚   β”œβ”€β”€ Header.css
9β”‚   └── index.js
10└── Footer/
11    β”œβ”€β”€ Footer.jsx
12    β”œβ”€β”€ Footer.css
13    └── index.js

Using

index.js
files to export components simplifies imports:

1// Instead of:
2import Button from './components/Button/Button';
3
4// You can write:
5import Button from './components/Button';

pages/

Components representing entire pages or views in the application. In projects with routing, each file in this directory typically corresponds to a specific URL path:

1pages/
2β”œβ”€β”€ Home/
3β”‚   β”œβ”€β”€ Home.jsx
4β”‚   └── Home.css
5β”œβ”€β”€ About/
6β”‚   β”œβ”€β”€ About.jsx
7β”‚   └── About.css
8└── Dashboard/
9    β”œβ”€β”€ Dashboard.jsx
10    └── Dashboard.css

assets/

Contains all static assets used in the application:

1assets/
2β”œβ”€β”€ images/
3β”‚   β”œβ”€β”€ logo.png
4β”‚   └── background.jpg
5β”œβ”€β”€ fonts/
6β”‚   β”œβ”€β”€ roboto.ttf
7β”‚   └── open-sans.ttf
8└── styles/
9    β”œβ”€β”€ variables.css
10    └── global.css

utils/

Utility functions and tools used throughout the application:

1utils/
2β”œβ”€β”€ formatDate.js
3β”œβ”€β”€ validation.js
4└── helpers.js

services/

Code responsible for communicating with external APIs or services:

1services/
2β”œβ”€β”€ api.js           # Basic API configuration
3β”œβ”€β”€ userService.js   # User-related queries
4└── productService.js # Product-related queries

hooks/

Custom React hooks that can be used in multiple places in the application:

1hooks/
2β”œβ”€β”€ useLocalStorage.js
3β”œβ”€β”€ useFetch.js
4└── useForm.js

context/

React contexts used for managing global state:

1context/
2β”œβ”€β”€ AuthContext.js
3β”œβ”€β”€ ThemeContext.js
4└── CartContext.js

Organization by feature vs. by type

There are two main approaches to code organization:

Organization by type (shown above)

Files are grouped by their type (components, hooks, services, etc.).

Organization by feature

Files are grouped by feature or part of the application:

1src/
2β”œβ”€β”€ auth/
3β”‚   β”œβ”€β”€ components/
4β”‚   β”œβ”€β”€ hooks/
5β”‚   β”œβ”€β”€ services/
6β”‚   └── context/
7β”œβ”€β”€ products/
8β”‚   β”œβ”€β”€ components/
9β”‚   β”œβ”€β”€ hooks/
10β”‚   β”œβ”€β”€ services/
11β”‚   └── utils/
12└── checkout/
13    β”œβ”€β”€ components/
14    β”œβ”€β”€ hooks/
15    └── services/

This structure is more scalable for large projects, especially when multiple people are working on them.

Naming conventions

Consistent naming is just as important as directory structure:

Components

  • Use PascalCase:
    Button.jsx
    ,
    UserProfile.jsx
  • JSX files should have the
    .jsx
    extension (not required, but helps with identification)

Hooks

  • Always start with "use":
    useLocalStorage.js
    ,
    useWindowSize.js

Contexts

  • Usually end with "Context":
    AuthContext.js
    ,
    ThemeContext.js

Services and utilities

  • Use camelCase:
    apiService.js
    ,
    formatDate.js

Styles in React

There are several approaches to organizing styles in React:

CSS Modules

1Button/
2β”œβ”€β”€ Button.jsx
3└── Button.module.css

Styled Components

1Button/
2└── Button.jsx  # Contains styled-components definitions

SCSS/SASS

1Button/
2β”œβ”€β”€ Button.jsx
3└── Button.scss

Folder structure best practices

In the React cosmic fleet, a well-organized folder structure is like an efficient logistics system on a spaceship - everything has its place and you can quickly find what you need. Here are proven practices for organizing React projects.

Feature-based vs Type-based organization

Two main approaches to organizing code in React projects:

Type-based organization

Files grouped by their technical role - all components together, all hooks together, etc.

1src/
2β”œβ”€β”€ components/
3β”‚   β”œβ”€β”€ Button/
4β”‚   β”œβ”€β”€ Card/
5β”‚   β”œβ”€β”€ Modal/
6β”‚   β”œβ”€β”€ UserProfile/
7β”‚   β”œβ”€β”€ ProductCard/
8β”‚   └── ShoppingCart/
9β”œβ”€β”€ hooks/
10β”‚   β”œβ”€β”€ useAuth.js
11β”‚   β”œβ”€β”€ useProducts.js
12β”‚   └── useCart.js
13β”œβ”€β”€ services/
14β”‚   β”œβ”€β”€ authService.js
15β”‚   β”œβ”€β”€ productService.js
16β”‚   └── cartService.js
17└── utils/
18    β”œβ”€β”€ validators.js
19    └── formatters.js

Advantages:

  • Simpler for small and medium projects
  • Easy to understand for new developers
  • Good for SPA applications without distinct modules

Disadvantages:

  • In large projects, it's hard to find all files related to a single feature
  • Components from different features are mixed together
  • Harder to scale across teams

Feature-based organization

Files grouped by business feature - all files related to authentication together, all product-related files together, etc.

1src/
2β”œβ”€β”€ features/
3β”‚   β”œβ”€β”€ auth/
4β”‚   β”‚   β”œβ”€β”€ components/
5β”‚   β”‚   β”‚   β”œβ”€β”€ LoginForm/
6β”‚   β”‚   β”‚   β”œβ”€β”€ RegisterForm/
7β”‚   β”‚   β”‚   └── PasswordReset/
8β”‚   β”‚   β”œβ”€β”€ hooks/
9β”‚   β”‚   β”‚   β”œβ”€β”€ useAuth.js
10β”‚   β”‚   β”‚   └── useSession.js
11β”‚   β”‚   β”œβ”€β”€ services/
12β”‚   β”‚   β”‚   └── authService.js
13β”‚   β”‚   β”œβ”€β”€ utils/
14β”‚   β”‚   β”‚   └── validators.js
15β”‚   β”‚   └── index.js
16β”‚   β”‚
17β”‚   β”œβ”€β”€ products/
18β”‚   β”‚   β”œβ”€β”€ components/
19β”‚   β”‚   β”‚   β”œβ”€β”€ ProductList/
20β”‚   β”‚   β”‚   β”œβ”€β”€ ProductCard/
21β”‚   β”‚   β”‚   └── ProductDetails/
22β”‚   β”‚   β”œβ”€β”€ hooks/
23β”‚   β”‚   β”‚   β”œβ”€β”€ useProducts.js
24β”‚   β”‚   β”‚   └── useProductFilters.js
25β”‚   β”‚   β”œβ”€β”€ services/
26β”‚   β”‚   β”‚   └── productService.js
27β”‚   β”‚   └── index.js
28β”‚   β”‚
29β”‚   └── cart/
30β”‚       β”œβ”€β”€ components/
31β”‚       β”‚   β”œβ”€β”€ ShoppingCart/
32β”‚       β”‚   β”œβ”€β”€ CartItem/
33β”‚       β”‚   └── Checkout/
34β”‚       β”œβ”€β”€ hooks/
35β”‚       β”‚   └── useCart.js
36β”‚       β”œβ”€β”€ services/
37β”‚       β”‚   └── cartService.js
38β”‚       └── index.js
39β”‚
40β”œβ”€β”€ shared/
41β”‚   β”œβ”€β”€ components/
42β”‚   β”‚   β”œβ”€β”€ Button/
43β”‚   β”‚   β”œβ”€β”€ Modal/
44β”‚   β”‚   └── Card/
45β”‚   β”œβ”€β”€ hooks/
46β”‚   β”‚   β”œβ”€β”€ useLocalStorage.js
47β”‚   β”‚   └── useDebounce.js
48β”‚   └── utils/
49β”‚       β”œβ”€β”€ formatDate.js
50β”‚       └── formatCurrency.js
51β”‚
52└── App.jsx

Advantages:

  • Easier to scale for large projects
  • All files related to a feature are in one place
  • Easier for team work (different teams can work on different features)
  • Easier to remove entire features
  • Better for code splitting

Disadvantages:

  • More complex for beginners
  • Requires more thought in design
  • Can lead to duplication for very small components

Naming conventions

Consistent naming is a key element of a well-organized project. In React, the following conventions apply:

Components - PascalCase

1// GOOD - PascalCase
2Button.jsx
3UserProfile.jsx
4ShoppingCart.jsx
5ProductCard.jsx
6
7// BAD - other conventions
8button.jsx
9user-profile.jsx
10shopping_cart.jsx

Why PascalCase?

  • Distinguishes React components from regular functions
  • Convention adopted across the entire React ecosystem
  • JSX requires PascalCase for components (custom tags)

Hooks - camelCase with "use" prefix

1// GOOD - camelCase with "use"
2useAuth.js
3useLocalStorage.js
4useWindowSize.js
5useProductFilters.js
6
7// BAD - missing "use" or wrong convention
8Auth.js
9local-storage.js
10WindowSize.js

Why "use" prefix?

  • React requires custom hooks to start with "use"
  • Allows React to validate hook rules
  • Immediately communicates that it's a hook

Contexts - PascalCase with "Context" suffix

1// GOOD - PascalCase with "Context"
2AuthContext.js
3ThemeContext.js
4CartContext.js
5UserPreferencesContext.js
6
7// BAD - missing "Context" or wrong convention
8auth.js
9theme-context.js
10Cart.js

Utils and services - camelCase

1// GOOD - camelCase
2formatDate.js
3validateEmail.js
4apiService.js
5authService.js
6
7// BAD - other conventions
8FormatDate.js
9validate-email.js
10API_Service.js

Where to keep different file types

Planning file locations in a project has enormous significance for its readability and scalability.

Hooks (custom hooks)

Feature-based approach:

1features/
2β”œβ”€β”€ auth/
3β”‚   └── hooks/
4β”‚       β”œβ”€β”€ useAuth.js
5β”‚       └── useSession.js
6└── products/
7    └── hooks/
8        └── useProducts.js
9
10shared/
11└── hooks/
12    β”œβ”€β”€ useLocalStorage.js  # Shared hook
13    β”œβ”€β”€ useDebounce.js      # Shared hook
14    └── useFetch.js         # Shared hook

Type-based approach:

1hooks/
2β”œβ”€β”€ useAuth.js
3β”œβ”€β”€ useSession.js
4β”œβ”€β”€ useProducts.js
5β”œβ”€β”€ useLocalStorage.js
6β”œβ”€β”€ useDebounce.js
7└── useFetch.js

Utils (utility functions)

Feature-based approach:

1features/
2β”œβ”€β”€ products/
3β”‚   └── utils/
4β”‚       β”œβ”€β”€ formatPrice.js
5β”‚       └── filterProducts.js
6└── auth/
7    └── utils/
8        └── validatePassword.js
9
10shared/
11└── utils/
12    β”œβ”€β”€ formatDate.js       # Shared
13    β”œβ”€β”€ debounce.js         # Shared
14    └── cloneDeep.js        # Shared

Type-based approach:

1utils/
2β”œβ”€β”€ formatPrice.js
3β”œβ”€β”€ formatDate.js
4β”œβ”€β”€ validatePassword.js
5β”œβ”€β”€ filterProducts.js
6β”œβ”€β”€ debounce.js
7└── cloneDeep.js

Types (TypeScript)

If you use TypeScript, you have several options for types:

Option 1: Next to the files that use them:

1components/
2└── Button/
3    β”œβ”€β”€ Button.tsx
4    β”œβ”€β”€ Button.types.ts     # Types only for Button
5    β”œβ”€β”€ Button.styles.ts
6    └── index.ts

Option 2: Centralized types:

1types/
2β”œβ”€β”€ user.types.ts
3β”œβ”€β”€ product.types.ts
4β”œβ”€β”€ cart.types.ts
5└── api.types.ts

Option 3: Hybrid (best):

1features/
2└── products/
3    β”œβ”€β”€ components/
4    β”œβ”€β”€ types/              # Feature-specific types
5    β”‚   β”œβ”€β”€ product.types.ts
6    β”‚   └── filter.types.ts
7    └── index.ts
8
9shared/
10└── types/                  # Shared types
11    β”œβ”€β”€ common.types.ts
12    └── api.types.ts

Example project structure - best practices

Combining feature-based for main features and type-based for shared resources:

1my-react-app/
2β”œβ”€β”€ public/
3β”œβ”€β”€ src/
4β”‚   β”œβ”€β”€ features/                    # Main features
5β”‚   β”‚   β”œβ”€β”€ auth/
6β”‚   β”‚   β”‚   β”œβ”€β”€ components/
7β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ LoginForm/
8β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ LoginForm.tsx
9β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ LoginForm.styles.ts
10β”‚   β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ LoginForm.test.tsx
11β”‚   β”‚   β”‚   β”‚   β”‚   └── index.ts
12β”‚   β”‚   β”‚   β”‚   └── RegisterForm/
13β”‚   β”‚   β”‚   β”œβ”€β”€ hooks/
14β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ useAuth.ts
15β”‚   β”‚   β”‚   β”‚   └── useSession.ts
16β”‚   β”‚   β”‚   β”œβ”€β”€ services/
17β”‚   β”‚   β”‚   β”‚   └── authService.ts
18β”‚   β”‚   β”‚   β”œβ”€β”€ types/
19β”‚   β”‚   β”‚   β”‚   └── auth.types.ts
20β”‚   β”‚   β”‚   └── index.ts
21β”‚   β”‚   β”‚
22β”‚   β”‚   β”œβ”€β”€ products/
23β”‚   β”‚   β”‚   β”œβ”€β”€ components/
24β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ ProductList/
25β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ ProductCard/
26β”‚   β”‚   β”‚   β”‚   └── ProductFilters/
27β”‚   β”‚   β”‚   β”œβ”€β”€ hooks/
28β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ useProducts.ts
29β”‚   β”‚   β”‚   β”‚   └── useProductFilters.ts
30β”‚   β”‚   β”‚   β”œβ”€β”€ services/
31β”‚   β”‚   β”‚   β”‚   └── productService.ts
32β”‚   β”‚   β”‚   β”œβ”€β”€ types/
33β”‚   β”‚   β”‚   β”‚   └── product.types.ts
34β”‚   β”‚   β”‚   └── index.ts
35β”‚   β”‚   β”‚
36β”‚   β”‚   └── cart/
37β”‚   β”‚       └── [similar structure]
38β”‚   β”‚
39β”‚   β”œβ”€β”€ shared/                      # Shared resources
40β”‚   β”‚   β”œβ”€β”€ components/
41β”‚   β”‚   β”‚   β”œβ”€β”€ Button/
42β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ Button.tsx
43β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ Button.styles.ts
44β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ Button.test.tsx
45β”‚   β”‚   β”‚   β”‚   └── index.ts
46β”‚   β”‚   β”‚   β”œβ”€β”€ Modal/
47β”‚   β”‚   β”‚   β”œβ”€β”€ Card/
48β”‚   β”‚   β”‚   └── Layout/
49β”‚   β”‚   β”‚
50β”‚   β”‚   β”œβ”€β”€ hooks/
51β”‚   β”‚   β”‚   β”œβ”€β”€ useLocalStorage.ts
52β”‚   β”‚   β”‚   β”œβ”€β”€ useDebounce.ts
53β”‚   β”‚   β”‚   └── useFetch.ts
54β”‚   β”‚   β”‚
55β”‚   β”‚   β”œβ”€β”€ utils/
56β”‚   β”‚   β”‚   β”œβ”€β”€ formatDate.ts
57β”‚   β”‚   β”‚   β”œβ”€β”€ validators.ts
58β”‚   β”‚   β”‚   └── helpers.ts
59β”‚   β”‚   β”‚
60β”‚   β”‚   β”œβ”€β”€ types/
61β”‚   β”‚   β”‚   β”œβ”€β”€ common.types.ts
62β”‚   β”‚   β”‚   └── api.types.ts
63β”‚   β”‚   β”‚
64β”‚   β”‚   └── constants/
65β”‚   β”‚       β”œβ”€β”€ routes.ts
66β”‚   β”‚       └── config.ts
67β”‚   β”‚
68β”‚   β”œβ”€β”€ layouts/                     # Layout components
69β”‚   β”‚   β”œβ”€β”€ MainLayout/
70β”‚   β”‚   β”œβ”€β”€ AuthLayout/
71β”‚   β”‚   └── DashboardLayout/
72β”‚   β”‚
73β”‚   β”œβ”€β”€ pages/                       # Page components (for routing)
74β”‚   β”‚   β”œβ”€β”€ HomePage/
75β”‚   β”‚   β”œβ”€β”€ ProductsPage/
76β”‚   β”‚   β”œβ”€β”€ ProductDetailPage/
77β”‚   β”‚   └── CartPage/
78β”‚   β”‚
79β”‚   β”œβ”€β”€ context/                     # Global contexts
80β”‚   β”‚   β”œβ”€β”€ AuthContext.tsx
81β”‚   β”‚   β”œβ”€β”€ ThemeContext.tsx
82β”‚   β”‚   └── CartContext.tsx
83β”‚   β”‚
84β”‚   β”œβ”€β”€ router/                      # Routing configuration
85β”‚   β”‚   β”œβ”€β”€ routes.tsx
86β”‚   β”‚   └── ProtectedRoute.tsx
87β”‚   β”‚
88β”‚   β”œβ”€β”€ styles/                      # Global styles
89β”‚   β”‚   β”œβ”€β”€ globals.css
90β”‚   β”‚   β”œβ”€β”€ variables.css
91β”‚   β”‚   └── reset.css
92β”‚   β”‚
93β”‚   β”œβ”€β”€ App.tsx
94β”‚   └── index.tsx
95β”‚
96β”œβ”€β”€ .eslintrc.js
97β”œβ”€β”€ .prettierrc
98β”œβ”€β”€ tsconfig.json
99β”œβ”€β”€ package.json
100└── README.md

File organization rules

  1. Maximum 3 levels rule
    • Avoid nesting deeper than 3 levels
    • If you need more, your structure is probably too complex
1// GOOD - 3 levels
2src/features/products/components/
3
4// BAD - too deep
5src/modules/ecommerce/features/products/ui/components/
  1. Export through index.ts
    • Every folder with a component has an index.ts
    • Simplifies imports and hides implementation details
1// components/Button/index.ts
2export { default } from './Button';
3export * from './Button.types';
4
5// Usage
6import Button from './components/Button';  // readable
7// instead of
8import Button from './components/Button/Button';  // verbose
  1. Colocation - keep together what is used together
    • Styles next to the component
    • Tests next to the component
    • Types next to the component
1Button/
2β”œβ”€β”€ Button.tsx          # Component
3β”œβ”€β”€ Button.styles.ts    # Styles
4β”œβ”€β”€ Button.test.tsx     # Tests
5β”œβ”€β”€ Button.types.ts     # Types
6└── index.ts            # Export
  1. Shared vs Feature-specific
    • If something is used in more than 2 places -> shared
    • If something is specific to a feature -> keep it in the feature

Practical tips for project structure

  1. Start simple - Don't over-engineer a complex structure at the beginning of a project. For a small application, type-based may suffice; only later refactor to feature-based as the project grows.

  2. Refactor when needed - Adapt the structure as the project develops. If a folder has more than 10-15 files, consider splitting it.

  3. Keep components small - Components should be small and focused on a single task. If a component has more than 200-300 lines, consider splitting it.

  4. Group related files - Keep files that are closely related together. If you always edit two files together, they should probably be in the same folder.

  5. Document - Add README.md to main directories to explain their purpose and structure.

  6. Consistency above all - Choose one style (feature-based or type-based) and stick to it throughout the project. The worst thing is mixing styles.

Tools that help with code organization

  1. ESLint - Helps maintain consistent code style and catch potential errors
  2. Prettier - Automatically formats code according to established rules
  3. Import sorter - Sorts imports for better readability
  4. Path aliases - Simplifies imports using aliases like @components, @utils
1// tsconfig.json or jsconfig.json
2{
3  "compilerOptions": {
4    "baseUrl": "src",
5    "paths": {
6      "@components/*": ["shared/components/*"],
7      "@features/*": ["features/*"],
8      "@utils/*": ["shared/utils/*"],
9      "@hooks/*": ["shared/hooks/*"]
10    }
11  }
12}
13
14// Usage
15import Button from '@components/Button';
16import { useAuth } from '@features/auth';
17import { formatDate } from '@utils/formatDate';

A well-organized project structure greatly facilitates work, especially in large teams and projects. Remember that there is no single "right" structure - the most important thing is that it is consistent, logical for your team, and scalable as the project grows.

Go to CodeWorlds→