Best practices to structure react app

Best practices to structure react app

A Deep Dive into React App Organization and Optimization (2024)

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:

  1. /auth:

    • Likely contains files related to authentication, such as authentication components, utilities, or services.
  2. /pages:

    • Contains pages of your application. Each page is associated with a route.
  3. /components:

    • A general directory for reusable components.

      • /ui: UI-specific components.

      • /forms: Components related to form elements.

  4. /data (Optional):

    • The data folder is similar to the assets 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).
  5. /hooks:

    • Contains custom React hooks, promoting code reuse and separation of concerns.
  6. /services:

    • Likely includes services responsible for interacting with external APIs or managing data.
  7. /public:

    • Reserved for static assets that need to be served as-is.
  8. /utils:

    • General utility functions and shared code.
  9. /__tests__:

    • Centralized location for test files.
  10. /libs:

    • Could contain external libraries or utilities used across the application.
  11. /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:

  1. Modularity:

    • Each feature is encapsulated within its own directory, promoting modularity and encapsulation of related functionality.
  2. Scalability:

    • The structure scales well with the growth of the application. New features can be added with minimal impact on existing code.
  3. Maintainability:

    • Easy maintenance as each feature has its own directory, making it clear where to find and update code related to a specific feature.
  4. Collaboration:

    • Enhances collaboration among developers as different teams or individuals can work on separate features without stepping on each other's toes.
  5. Testability:

    • Tests are closely located to the code they are testing, making it easy to find and run tests for specific features.
  6. Consistency:

    • Enforces naming and structural consistency, reducing cognitive overhead and making the codebase more predictable.
  7. Reduced Coupling:

    • Reduces dependencies and coupling between different features, making it easier to reason about and update specific functionality.
  8. 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 ❤

Did you find this article valuable?

Support Ashish Jaiswar by becoming a sponsor. Any amount is appreciated!