JavaScript and React course Β· Module 1: React Basics
React project structure
In this lesson8
A well-organized project structure is crucial for code maintainability, easy navigation, and scalability of React applications. In this lesson, we'll look at best practices for structuring React projects and understand how to organize files and components.
Basic React project structure
Whatever the tool, a larger React project (here a Vite one) usually has a similar structure:
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.jsx # Main application component
13β βββ main.jsx # Application entry point
14βββ index.html # The HTML page the app is loaded into
15βββ vite.config.js # Vite configuration
16βββ .gitignore # Files ignored by Git
17βββ package.json # Project info and dependencies
18βββ README.md # Project documentationLet'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.cssGrouped 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.jsUsing 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.cssassets/
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.cssutils/
Utility functions and tools used throughout the application:
1utils/
2βββ formatDate.js
3βββ validation.js
4βββ helpers.jsservices/
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 querieshooks/
Custom React hooks that can be used in multiple places in the application:
1hooks/
2βββ useLocalStorage.js
3βββ useFetch.js
4βββ useForm.jscontext/
React contexts used for managing global state:
1context/
2βββ AuthContext.js
3βββ ThemeContext.js
4βββ CartContext.jsOrganization 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
.jsxextension (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.cssStyled Components
1Button/
2βββ Button.jsx # Contains styled-components definitionsSCSS/SASS
1Button/
2βββ Button.jsx
3βββ Button.scssFolder 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.jsAdvantages:
- 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.jsxAdvantages:
- 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.jsxWhy 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.jsWhy "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.jsUtils 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.jsWhere 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 hookType-based approach:
1hooks/
2βββ useAuth.js
3βββ useSession.js
4βββ useProducts.js
5βββ useLocalStorage.js
6βββ useDebounce.js
7βββ useFetch.jsUtils (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 # SharedType-based approach:
1utils/
2βββ formatPrice.js
3βββ formatDate.js
4βββ validatePassword.js
5βββ filterProducts.js
6βββ debounce.js
7βββ cloneDeep.jsTypes (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.tsOption 2: Centralized types:
1types/
2βββ user.types.ts
3βββ product.types.ts
4βββ cart.types.ts
5βββ api.types.tsOption 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.tsExample 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.mdFile organization rules
- 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/- 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- 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- 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
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.
Refactor when needed - Adapt the structure as the project develops. If a folder has more than 10-15 files, consider splitting it.
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.
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.
Document - Add README.md to main directories to explain their purpose and structure.
Consistency above all - Once you choose a layout (by type, by feature, or the hybrid described above), stick to it throughout the project. The worst thing is mixing styles at random.
Tools that help with code organization
- ESLint - Helps maintain consistent code style and catch potential errors
- Prettier - Automatically formats code according to established rules
- Import sorter - Sorts imports for better readability
- 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.
Code for this lesson: App.jsx
1import React, { useState } from 'react';
2
3// Demonstration of Vite + React project structure
4function ViteProjectDemo() {
5 const [activeFile, setActiveFile] = useState('App.jsx');
6
7 const fileStructure = [
8 { name: 'node_modules/', type: 'folder', description: 'Installed packages' },
9 { name: 'public/', type: 'folder', description: 'Static files' },
10 { name: ' vite.svg', type: 'file', description: 'Vite logo' },
11 { name: 'src/', type: 'folder', description: 'Source code' },
12 { name: ' assets/', type: 'folder', description: 'Assets (images, fonts)' },
13 { name: ' App.jsx', type: 'file', description: 'Main component', active: true },
14 { name: ' App.css', type: 'file', description: 'Component styles' },
15 { name: ' main.jsx', type: 'file', description: 'Entry point' },
16 { name: ' index.css', type: 'file', description: 'Global styles' },
17 { name: 'index.html', type: 'file', description: 'Main HTML (in the root directory!)' },
18 { name: 'vite.config.js', type: 'file', description: 'Vite configuration' },
19 { name: 'package.json', type: 'file', description: 'Project configuration' },
20 ];
21
22 const fileContents = {
23 'App.jsx': `import { useState } from 'react'
24import './App.css'
25
26function App() {
27 const [count, setCount] = useState(0)
28
29 return (
30 <>
31 <h1>Vite + React</h1>
32 <div className="card">
33 <button onClick={() => setCount(c => c + 1)}>
34 count is {count}
35 </button>
36 </div>
37 </>
38 )
39}
40
41export default App`,
42 'main.jsx': `import React from 'react'
43import ReactDOM from 'react-dom/client'
44import App from './App.jsx'
45import './index.css'
46
47ReactDOM.createRoot(
48 document.getElementById('root')
49).render(
50 <React.StrictMode>
51 <App />
52 </React.StrictMode>,
53)`,
54 'vite.config.js': `import { defineConfig } from 'vite'
55import react from '@vitejs/plugin-react'
56
57export default defineConfig({
58 plugins: [react()],
59})`,
60 'package.json': `{
61 "name": "my-app",
62 "version": "0.0.0",
63 "type": "module",
64 "scripts": {
65 "dev": "vite",
66 "build": "vite build",
67 "preview": "vite preview"
68 },
69 "dependencies": {
70 "react": "^18.2.0",
71 "react-dom": "^18.2.0"
72 },
73 "devDependencies": {
74 "@vitejs/plugin-react": "^4.2.0",
75 "vite": "^5.0.0"
76 }
77}`
78 };
79
80 return (
81 <div style={{
82 fontFamily: 'Arial, sans-serif',
83 maxWidth: '1000px',
84 margin: '0 auto',
85 padding: '20px'
86 }}>
87 <header style={{
88 background: 'linear-gradient(135deg, #61dafb 0%, #21a1f1 100%)',
89 padding: '30px',
90 borderRadius: '12px',
91 color: 'white',
92 textAlign: 'center',
93 marginBottom: '30px'
94 }}>
95 <h1 style={{ margin: 0, fontSize: '2rem' }}>Vite + React</h1>
96 <p style={{ margin: '10px 0 0 0', opacity: 0.9 }}>
97 Project structure and basic files
98 </p>
99 </header>
100
101 <div style={{
102 backgroundColor: '#1e1e1e',
103 borderRadius: '8px',
104 padding: '20px',
105 marginBottom: '30px',
106 fontFamily: 'monospace'
107 }}>
108 <div style={{ color: '#888', marginBottom: '10px' }}>Terminal:</div>
109 <div style={{ color: '#00ff00' }}>$ npm create vite@latest my-app -- --template react</div>
110 <div style={{ color: '#888', marginTop: '5px' }}>Scaffolding project in /my-app...</div>
111 <div style={{ color: '#00ff00', marginTop: '5px' }}>Done. Now run: cd my-app && npm install && npm run dev</div>
112 </div>
113
114 <div style={{ display: 'grid', gridTemplateColumns: '300px 1fr', gap: '20px' }}>
115 <div style={{
116 backgroundColor: '#252526',
117 borderRadius: '8px',
118 padding: '15px'
119 }}>
120 <h3 style={{ color: '#cccccc', margin: '0 0 15px 0', fontSize: '14px' }}>
121 EXPLORER
122 </h3>
123 {fileStructure.map((item, index) => (
124 <div
125 key={index}
126 onClick={() => item.type === 'file' && setActiveFile(item.name.trim())}
127 style={{
128 padding: '8px 10px',
129 color: item.type === 'folder' ? '#dcb67a' : '#9cdcfe',
130 cursor: item.type === 'file' ? 'pointer' : 'default',
131 backgroundColor: activeFile === item.name.trim() ? '#094771' : 'transparent',
132 borderRadius: '4px',
133 fontSize: '14px',
134 fontFamily: 'monospace'
135 }}
136 >
137 {item.type === 'folder' ? 'βΈ' : 'β’'} {item.name}
138 </div>
139 ))}
140 </div>
141
142 <div style={{
143 backgroundColor: '#1e1e1e',
144 borderRadius: '8px',
145 padding: '20px'
146 }}>
147 <div style={{
148 color: '#888',
149 fontSize: '12px',
150 marginBottom: '10px',
151 borderBottom: '1px solid #333',
152 paddingBottom: '10px'
153 }}>
154 {activeFile}
155 </div>
156 <pre style={{
157 color: '#d4d4d4',
158 margin: 0,
159 fontSize: '14px',
160 lineHeight: '1.5',
161 overflow: 'auto'
162 }}>
163 {fileContents[activeFile] || '// Select a file from the list'}
164 </pre>
165 </div>
166 </div>
167
168 <div style={{
169 marginTop: '30px',
170 padding: '20px',
171 backgroundColor: '#fff3e0',
172 borderRadius: '8px'
173 }}>
174 <h3 style={{ color: '#e65100', marginTop: 0 }}>Startup commands</h3>
175 <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '15px' }}>
176 <div style={{ backgroundColor: 'white', padding: '15px', borderRadius: '8px' }}>
177 <code style={{ color: '#e65100' }}>npm run dev</code>
178 <p style={{ margin: '5px 0 0 0', fontSize: '14px', color: '#666' }}>
179 Starts the development server
180 </p>
181 </div>
182 <div style={{ backgroundColor: 'white', padding: '15px', borderRadius: '8px' }}>
183 <code style={{ color: '#e65100' }}>npm run build</code>
184 <p style={{ margin: '5px 0 0 0', fontSize: '14px', color: '#666' }}>
185 Builds the production version
186 </p>
187 </div>
188 </div>
189 </div>
190 </div>
191 );
192}
193
194export default function App() {
195 return <ViteProjectDemo />;
196}Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What is the main purpose of the src/ folder in a React project?
2. In which folder are the main React source files located?
3. Where is the index.html file located in a React project created with Vite?
Hands-on tasks in the game
- Vertical ordering
Arrange the file hierarchy of a React application:
- Click in order
Arrange the React development workflow:
- Code editor
In App.jsx, create the basic structure of a component: a React import, a functional App component that returns JSX, and a default export (export default).