There is no right or wrong way when creating a React application. Being a library react doesn't have any predefined structure for an application, it gives us a lot of freedom to do things. Most of the time what people tend to do is create a components
folder and use that as a place to store your complete application, this is not very scalable solution and the code becomes difficult to test or add features to. I like to keep my code structure in a way so that they are easy to test, being very obvious which makes adding features easy.
I am going to show you a way in which you can create a scalable architecture for a production React applications.
Tech Stack
Following are the technologies I generally use while creating a production ready scalable react application:
- React with Hooks
- Redux for state management
- React Router
- CSS Modules or Sass/Scss
- Jest and React Testing Library
Build tools and Linting
I generally use Create React App for bootstrapping my react application, and add some custom configuration using craco (Create React App Configuration Override).
When a react application grows in size and complexity various problems also arise, managing imports is one of them. Aliases just makes importing from anywhere inside the project easy, also moving files is a very less hassle, you can easily do something like import Component from @components/my-component
.
You can set up craco in you project and add the following to craco.config.js
to enable aliases:
const path = require("path");
module.exports = {
webpack: {
alias: {
'@components': path.resolve(__dirname, "src/components/"),
'@images': path.resolve(__dirname, "src/assets/images/")
}
}
}
Even your IDE can be set up to use these while auto-importing components/files.
As Create React App getting started template already comes bundled with eslint I tend to leave the linting to it, and only add custom configurations if required to the eslint config file, or else we are good to go.
Directory Structure
Following are the top level folders:
- assets - place to keep all your static assets from images, logos, splash, etc.
- components - shared/reusable components, like buttons, form components(select, input...), layout wrappers
- libs - for javascript modules, and also for custom hooks
- store - global redux store
- utils - utilities, helper methods
- pages - to store all the main views for our app, most of the app is contained here
- contexts - it is optional, but is used to store all the contexts used globally
└── /src
├── App.js
├── /assets
├── /components
├── /contexts
├── index.js
├── /libs
├── /pages
├── /store
└── /utils
Apart from these, you can also add types
if using react with typescript, but keep in mind these are a bit flexible, like pages
can be called views
, also you can separate javscript modules and hooks in two different directories.
Components
All the shared components across the projects are stored here, also further grouping is done by type for example - forms
, layout
, etc.
Components have a single component.js
file with named exports, its better to avoid default exporting which might create file name conflicts. Most of the times we are trying to create our own form system or layout from bindings from different libraries like Formik and Material UI, or ant design. So, I prefer to keep the files together in a folder so its easy to find them and work with them. Following is in a way I create files for a component:
- Component.js - Your React component goes here
- component.module.css/component.scss - All the styles related to this component
Here we have one thing to consider, you can either create a test file here or create a similar structure in your __tests__
folder like following:
└── __tests__
└── Components
└── Component.test.js
Or, As I prefer them to be together in the Components
folder itself with the Component
just makes them easier to find and concentrate on them.
- Component.test.js - tests cases related to your component
└── components
├── layout
| └── container
│ ├── Container.js
│ ├── container.scss
│ └── Container.test.js
└── navigation
├── NotFound
│ ├── NotFound.js
│ └── NotFound.test.js
├── PvtRoute
│ ├── PvtRoute.js
│ └── PvtRoute.test.js
├── RootNavigator.js
└── RootNavigator.test.js
Libs
This directory will host all our javascript modules/services which will be used throughout our application. Not only that but all our custom hooks can also be stored inside this directory. Following can be the way to setup the folder:
└── src
└── libs
├── localStorage.js
├── useAuthentication.js
└── useUser.js
Store
This contains our global store, for our case it will be Redux. We will have folders for each of our features which will contain the reducers, actions and as well as tests. We will also have one index.js
file which will be our rootReducer
, which will be on the top most level of the store folder. The files can be created in the following manner:
└── src
└── store
├── index.js
├── recipes
│ ├── recipesAction.js
│ └── recipesReducer.js
└── user
├── userAction.js
└── userReducer.js
It is very extensible method can can be used with Redux Toolkit or with Redux Sagas, in place of reducers
and action
keywords it could be slices
or sagas
.
Utils
Here we will store all our utilities, which will be used by the entire application. Not all code bases need a utils folder, but I recommend to have one to store all the global configurations and methods which can be used elsewhere in the app. It can contain a helpers.js
file which can store all the different functions used. Another for form validation we can create, validations.js
, and to store constants we can have one constants.js
└── src
└── utils
├── constants.js
├── helpers.js
└── validations.js
Pages or Views
Here lives are our main chunk of the application, it can also be called views
as each page is like a "view" in an application. Anything inside a view is a page which will be used in a specific view - AddRecipe
page which will only be used in context to /recipes
route, and UserProfile
page will only be used on the /users
route.
The main advantage of storing our pages in this way, instead putting all of the pages together inside components
or pages
is that it makes it very easy to understand the app structure. If there are nested routes, you can always add a nested views folder within the main route.
└── src
└── pages
└── Recipes
└── AddRecipe
├── AddRecipe.js
└── AddRecipe.test.js
Conclusion
This is my personal preference to setup a react based project/webapp, which scales when the code base becomes huge in production and its hard to manage resources.
Would love to know more, how do you guys setup your react applications?
Top comments (0)