How I structure my React projects
Tags: react, redux, javascript, webdev
Published: 2022-02-25 18:18:24

Finding the correct path to import a component is always a big headache in React development. Laying out a proper structure for your React project ahead helps you and your team in many ways through the development process:

1. A better understanding of how files are connected and work together
2. Easier maintenance as the project scales, avoiding restructuring and modifying all the routes and import paths
3. Higher productivity (better readability, finding the source of bugs, etc.)
4. A clear organization that cures your OCD

Here is how I put my React project into a clean and practical structure.

## Src

```
src
├── components
├── pages
├── slices
├── utils
├── App.js
├── index.js
├── routes.js
└── store.js
```


As common, App.js and index.js are entries of the React project, routes.js and store.js are entries of React-router and Redux. The four folders above are the essential lego bricks that hold up the project code.

## Components
```
components
├── guards
│   └── AuthGuard.js
├── layout
│   └── NavBar
│       ├── components
│       │   ├── NavItem.js
│       │   └── NavSection.js
│       └── index.js
├── modules
│   └── cards
│       ├── ItemCard.js
│       └── UserCard.js
└── widgets
    └── buttons
        ├── PrimaryButton.js
        └── SecondaryButton.js
```

`/components` contains global components and atomic or modular components.

Global components like `AuthGuard.js` and `NavBar` are parent components of all pages in the router. For example, `AuthGuard.js` wraps around components that need authentication, checks if the user is authenticated, and jumps to the login page if not.

Atomic components like PrimaryButton.js are the smallest UI components that will be reused in modules and pages. Modular components like `UserCard.js` are modules that contain multiple widgets as a component to serve a specific function, which are reused in more than one page.

## Pages
```
pages
├── Login.js
└── account
    ├── index.js
    ├── profile
    │   ├── components
    │   │   ├── ProfileCover.js
    │   │   └── ProfileDetail.js
    │   └── index.js
    └── settings
        ├── components
        │   ├── AccountSettings.js
        │   └── NotificationSettings.js
        └── index.js
```
`/pages` contains pages shown on the website. It should be structured in a similar way as the router to give you a better understanding of how the real website would be browsed. This is also similar to the Next.js approach.

For example, the outer folder `/account` is an entrance on the navbar, which includes two pages profile and settings. Each page folder has an `index.js` (the page itself), and contains modules that made up this page in the /components folder.

A clear way for me to organize code is that only reusable components are in `/components`, while components built for a specific page are under `/pages/[page-name]/components`.

It’s important to separate the components in pages once you find them reusable. It’s even better if you are taking a bottom-up approach and build the components first if you find them potentially reusable.

## Slices

```
slices
├── itemSlice.js
└── userSlice.js
```
Now for the logic side. I use Redux Toolkit which allows me to handle Redux actions and reducers easily in one place called a “slice”. It also comes with many useful middlewares like createAsyncThunk.

For example, the `userSlice.js` handles user authentication like this:

```javascript
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
import setAuthToken from '../utils/setAuthToken';

// login action
export const login = createAsyncThunk('users/login', async (email, password) => {
  const config = {
    headers: { 'Content-Type': 'application/json' },
  };
  const body = JSON.stringify({ email, password });

  try {
    const res = await axios.post('/api/user/login', body, config);
    await localStorage.setItem('token', res.data.token);
    return res.data;
  } catch (err) {
    console.log(err.response.data);
  }
});

const userSlice = createSlice({
  name: 'userSlice',
  initialState: {
    loading: false,
    user: null,
  },
  reducers: {},
  extraReducers: {
    // login reducers
    [login.pending]: (state) => {
      state.loading = true;
    },
    [login.fulfilled]: (state, action) => {
      state.user = action.payload.user;
      setAuthToken(action.payload.token);
      state.loading = false;
    },
    [login.rejected]: (state) => {
      state.loading = false;
    },
  },
});

export default userSlice.reducer;
```

`/slices` basically contains all the Redux Toolkit slices. You can think of `/slices` as a central place that governs the global state and the specified functions to modify it. Each slice that handles an aspect of the app’s global state should be separated into one file.

## Utils

```
utils
├── objDeepCopy.js
└── setAuthToken.js
```
Lastly, `/utils` contains files that deal with logic to fulfill a certain function. They are functional pieces commonly used in many places in the project.

For example, `setAuthToken.js` gets a token and set or delete the `x-auth-token` axios header. It’s used in userSlice.js above.

---

There are other structures based on different tech stacks. For example, you might want to have `/contexts` and `/hooks` folders if you are using useContext and useReducers instead of Redux.

This is only one possible structure style among many options, and definitely not the best one. After all, the best React project structure is the one that fits your development style, and you will finally find the one suitable after many adjustments.