Best practices to structure react app
As a developer, I often find it challenging to organize files and folders in React. I used to wonder what would be the perfect way to design your folder structures along with the naming conventions. Therefore I started doing my research and came up with my approach of organizing files. Although React doesn’t have opinions on how you put files into folders. You can come up with your design structure based on the problem you are trying to solve.
If you aspire to improve as a React developer, picking a better file structure will reduce the overhead of work and complexity therefore please go through the blog and feel free to customize the structure according to your need.
Why you should organize files in React?
Organizing files in a React application is essential for clarity and efficiency. A well-structured file system improves code readability, simplifies collaboration among team members, and enhances the modularity of your components. As your project grows, a thoughtful organization strategy becomes crucial for maintaining scalability and ease of maintenance in the long run.
Adaptable File Structures for Your React App Size
Choosing the correct file structure depends on the size of your application. If you’re just starting a project, don’t spend more than five minutes on choosing a file structure. Pick any of the above approaches (or come up with your own) and start writing code! You’ll likely want to rethink it anyway after you’ve written some real code
Small-to-Medium Apps
/src
|-- /auth
|-- /pages
| |-- home.js
| |-- dashboard.js
|-- /components
| |-- /ui
| | |-- Button.js
| |-- /forms
| |-- LoginForm.js
| |-- SignupForm.js
| |-- Navbar.js
|-- /hooks
| |-- useLocalStorage.js
| |-- useApiFetch.js
|-- /services
| |-- authService.js
| |-- dataService.js
|-- /public
| |-- index.html
| |-- favicon.ico
|-- /utils
| |-- commonUtils.js
| |-- validationUtils.js
|-- /__tests__
| |-- ui.test.js
| |-- authService.test.js
|-- /libs
| |-- externalLibrary1.js
| |-- externalLibrary2.js
|-- /store
| |-- /entities
| | |-- userEntity.js
| | |-- postEntity.js
| |-- /middleware
| |-- loggerMiddleware.js
| |-- analyticsMiddleware.js
The provided directory structure appears well-organized, reflecting a modular and feature-based approach. Here's a brief breakdown of each directory:
/auth:
- Likely contains files related to authentication, such as authentication components, utilities, or services.
/pages:
- Contains pages of your application. Each page is associated with a route.
/components:
A general directory for reusable components.
/ui: UI-specific components.
/forms: Components related to form elements.
/data (Optional):
- The
data
folder is similar to theassets
folder, but this is for storing our data assets such as JSON files that contain information used in our code (store items, theme information, etc).
- The
/hooks:
- Contains custom React hooks, promoting code reuse and separation of concerns.
/services:
- Likely includes services responsible for interacting with external APIs or managing data.
/public:
- Reserved for static assets that need to be served as-is.
/utils:
- General utility functions and shared code.
/__tests__:
- Centralized location for test files.
/libs:
- Could contain external libraries or utilities used across the application.
/store:
State management related files. Redux related folders.
/entities: Entities or data models used in the application state.
/middleware: Middleware for state management.
Large Apps
/src
|-- /assets
|-- /components
| |-- /Button
| | |-- Button.js
| | |-- Button.css
| | |-- Button.test.js
| | |-- index.js
|-- /features
| |-- /HomePage
| | |-- /components
| | |-- /hooks
| | |-- /services
| | |-- /utils
| | |-- index.js
| |-- /Posts
| | |-- /components
| | |-- /hooks
| | |-- /services
| | |-- /utils
| | |-- index.js
|-- /hooks
|-- /services
|-- /public
|-- /utils
|-- /libs
|-- /pages
| |-- /__test__
| | |-- /components
| | | |-- Button.test.js
| | | |-- FeatureSpecificComponent.test.js
| | |-- /features
| | | |-- HomePage.test.js
| |-- home.js
|-- /store
| |-- /entities
| | |-- userEntity.js
| | |-- postEntity.js
| |-- /middleware
| | |-- loggerMiddleware.js
| | |-- analyticsMiddleware.js
|-- /config
The two folder structures seem similar, but the features
folder is a major difference. It's a more efficient way to group similar code and avoids overlaps found in the pages
folder of the intermediate structure. I'll only cover the changes between these two structures as many folders are repeated.
features
The key shift here is the features
folder, which groups by feature rather than page. This makes it easier for developers to add or modify features. Each feature has its own folder with a structure similar to the src
folder and an index.js
file. This layout allows for organized code by type, all kept together.
The index.js
file serves to expose a public API for each feature and limits access to private code. This helps maintain a smaller global code footprint and simplifies feature usage with a limited API. It's even possible to enforce an ESLint rule to disallow any import from a feature
folder that doesn’t come from index.js
..
{
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@/features/*/*"] }]
}
}
Above code from Bullet Proof React.
This import rules utilizes absolute imports (which I recommend using on larger projects). You can set this up by using a .jsconfig
or .tsconfig
file with the following code.
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@features/*": ["src/features/*"],
}
}
}
Benefits of Feature-Driven Structure:
Modularity:
- Each feature is encapsulated within its own directory, promoting modularity and encapsulation of related functionality.
Scalability:
- The structure scales well with the growth of the application. New features can be added with minimal impact on existing code.
Maintainability:
- Easy maintenance as each feature has its own directory, making it clear where to find and update code related to a specific feature.
Collaboration:
- Enhances collaboration among developers as different teams or individuals can work on separate features without stepping on each other's toes.
Testability:
- Tests are closely located to the code they are testing, making it easy to find and run tests for specific features.
Consistency:
- Enforces naming and structural consistency, reducing cognitive overhead and making the codebase more predictable.
Reduced Coupling:
- Reduces dependencies and coupling between different features, making it easier to reason about and update specific functionality.
Tooling Support:
- Some development tools and IDEs offer improved support for navigation and operations when files are organized in a feature-driven manner.
Ending
Your feedback is incredibly important to me, so please feel free to share your thoughts in the comments below.
If you enjoyed this article, don't hesitate to give it a round of applause 👏. Your support keeps the conversation going and encourages me to create more content for you.
Let's stay connected! Reach out to me on Twitter and LinkedIn.
Curious about my latest projects? Explore my portfolio ashishjaiswar.com to see more of my work.
Meet you on the next blog. Enjoy Coding ❤
Subscribe to my newsletter
Read articles from Ashish Jaiswar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Ashish Jaiswar
Ashish Jaiswar
I am a developer from Mumbai, India. Experienced in web development. I love building with web apps.