How to organise a complex React project

Organise a complex React project with a realistic folder structure
How to organise a complex React project

When it comes to starting a new project that you know is going to be big, a sound file structure is a must to nail in the first setup and you as a senior developer need to set up the file system so your team knows how to develop every feature.

In this article I’m going to go through 3 examples: e-commerce, blog, and social media. Indicating which file structure is best for them. This is my opinion and I haven’t asked ChatGPT to write this for me so take it with a pinch of salt and use your own judgement. By the end you’ll know: folder layout, what goes in Redux, how e-commerce / blog / social differ.

The first and most important advice that I want to give you is that you don’t use the frequently messy: pages, services, css, components, hooks, store… structure, I know you’ve used it many times in the past but it is crucial that you structure with these 2 big ideas in mind: features and shared code. Features indicate what the website does, cart, product list, checkout for e-commerce and blog and users for a blog, and social feed and post and user wall for a social media site. After separating the main features you want to expose what is shared within the app: components, utils, api, config. After that you want to have misc folders: types, store and assets.

If you’re using a framework like Next.js or React Native you will have a handy app folder for your url structure, this is very useful to have the page components grouped by url, but you should still use the features structure. What you don’t want to have in the app folder is shared components, and code belonging to the state. Pages should read like a table of contents 10 to 30 lines that import from the features folder. It’s a good place to have the server side file structure but you should keep the reusable functions in the shared folder (making sure these are server side functions only).

A good folder structure for a feature

a components folder for shared components within the feature only, and the same vibe for hooks, types, tests and api folders, but remember to move to the shared folder functions that are used across several features.

A good folder structure for the shared folder can be

  • ui/ where your atomic components live: buttons, headings, cards… but specific feature components do not: add to cart button, number of likes badge, user card…
  • libs/ where your plain functions live, single file for each and if you want add folder for all the tests or have them near your functions.
  • api/ where your API calls live, these are the lowest level api calls live, the GETs and POSTs that just receive the parameters to send and return formatted or ready to be consumed data. These functions are not to be confused with the data layer api functions which make use of a server side cache and can do data manipulation, these should be the ones communicating with that (GraphQL…). These functions should be in charge of the auth headers and error handling. Do not put the server side functions: getUserByID or checkout, they should live in the features folder.
  • hooks/ useMediaQuery, useDebounce, useLocalStorage. Truly global logic. Tests can live in a common folder or with each hook.
  • types/ Only global types: User, Product, ApiError. Feature types should live within each feature: CheckoutError, ProductToBuy…
  • config/ and constants/ files that use the process.env logic, never store actual API keys here.
  • styles/ themes, global.css, mixins, breakpoints, variables. Component CSS should live in the features folder.
  • public/ for files accessed via an absolute url
  • assets/ for files imported in the code, like SVGs

State management

A good folder structure if using Redux can be to add a state/ folder to the project with an index file, a rootReducer and hook files and then having .slice.ts .selectors.ts and .types.ts for each feature. These files get wired together in the state/ folder’s files. You should also add a ui state slice for things like modal opened, side drawer, themes…

What a senior developer should know is what goes and doesn’t go into the state, when we have social media posts that need to be kept in the state for backwards and forwards navigation and like and comment, we do need to keep them in a global state, when we have a blog post that doesn’t have any interaction we should use server side rendering and not store it in the state. We should also not store the value of an input and update it on every character entered.

Another good practice is to put cross-cutting Redux middleware and listeners in a central store/ (or shared/store/) layer, and keep feature folders focused on slices, selectors, and UI, not one-off side effects scattered across components.

Different examples

E-commerce:

  • Features: cart, checkout, catalog, product, account, search, admin.
  • State: we will have a global state for the cart and different UI toggles, then we don’t need to keep the catalog in a state if we’re using server side rendering, and store in the cache a catalog list and product details.
  • And the last special point is the payment flow, isolate it from the rest of the website for security.
  • Testing is important to do on price calculations, coupon discounts and cart object manipulation, making sure that we always send to the checkout flow what we need to send.

Blog:

  • Features: article list, article, user account, search articles, search users. It’s a good idea to use a CMS for article creation and store on server side cache the content of each article
  • We don’t need a global state for a simple blog site. Just SSR of the articles.
  • We should serve the images in a dynamic way, several sizes for mobile and desktop and possibly a low resolution one to render before the main article one if the connection is slow. We must preload the main article images from the most clicked articles if we’re in the article list page so the users don’t wait for it to load when you access the articles. It’s very important that we have good scores on SEO and performance so preventing huge amounts of javascript to be loaded at the beginning if they’re not necessary for a first time rendering is a good idea at this point. If we’re accessing the main article list we don’t need the react code to display a user popup, dropdown menu, side drawer to see the list, we can defer these js modules for when the site has finished loading.

Social media

  • Features: feed, post, profile, messaging, notifications, media-upload, search.
  • State: we need a snappy state for the post feed for forwards and backwards navigation and reconciliation techniques for when the feed has changed, rendering techniques for when we’re scrolling up and down (not display posts but a placeholder skeleton div on posts outside the viewport) and middleware to normalise entities (post, user, comments…) so what we receive from the backend can be manipulated in the front-end in a different shape.

I hope you’ve liked this article and let’s see each other again on the nets.