A vision about how to don’t get lost in your own application
It’s been two years time that I’ve met React and immediately fell in love with it. The way that React works, as a declarative, not-so-large library created exclusively to build rich User Interfaces provides everything as necessary (plus a rich ecosystem with tons of add-ons). As it’s read in the documentation, React is very unopinionated about how you might structure your project and choose your library stack, although the above mentioned document shows some recommendations.
This article was born to give enlightening recommendation for front-end developers on how you should organize your project if you want it to feel less painful and more scalable/clean as well. I don’t think that’s a rule, though. Feel free to use it, or, if you know a better way or have suggestions, just leave it in the comments! :)
In my current project, at the company for which I am working, I'm using something like this:
You must be thinking: "what the heck?"
I know. That's a lot of stuff going on. Don't worry: we'll get through it.
Lets start from the top:
Pretty self-explanatory. Should serve as the folder to place functions that call external resources (usually REST apis). The
users. file must have an
export default statement, for example.
Explains itself as well. Not much to do. If you're working with
create-react-app, should be easy to just import it and bundle within your JS or extracted to their own files in the
The global/common components folder. Every component that might be used in two or more views are put in this folder. Every component has its own folder,
styles.module.scss for the component styles. I'll come on the
styles.module.scss part later. For now, just keep reading.
I prefer to keep components simple, declared as arrow functions. This way, I somehow manage to "polite myself" against abusing the component responsibility (i.e. overusing state). It's a rare case when components do use state, but sometimes it is necessary. Also, some may include nested components (like a dropdown menu or so).
Now, this part is recommended only if you're thinking about internationalizing your React app. Later on I'll talk about the stack, but it's important for now: I'm using i18n-js as it's quite easy to use. The
index.js of the
lang folder looks like this:
keys folder has the constants in order to prevent typos and get them on-the-fly with a Linter, for example. A basic
index.js file would be:
And, in the
With that defined, when you want to use a localized message, just import the default function, the desired key and then invoke that function. Like this:
And let's not forget to define the translations in the corresponding folders. I'll just make it in english for the sake of brevity:
And now, in the
There's a clear explanation on this Stack Overflow answer about why you should use constants. Enough for i18n. Lets move on!
Now that's something that is not entirely related to React, but I, particularly, like to have a lib folder to put stuff like validation, some helpers/utils, etc. Some might argue that the
api folder may be there as well. Fair enough! It's up to you.
This folder is only needed if you're using Redux. I don't know much about MobX — never used — but it should be different.
Keep in mind that, while Redux is a great addition, you might not need it.
These subfolders should be pretty self-explanatory, but there are a few gotchas. Bear with me!
/actions: the action creators folder. In that folder we must put the functions that are bound to the connected component.
/definitions: that are basically the "action types" constants. But I prefer to call it "definitions" and put it outside the
actions folder because those definitions might be used in the reducers and in the sagas as well.
/reducers: the reducers folder. Every reducer should have its own file and the
index.js file is responsible for combining them into one.
/sagas: the same thing as the reducers, but for the sagas. The
index.js file contains the
rootSaga, the saga responsible for wiring it all up. Example:
NOTE: It's ok to not use Saga if you don't want to. Not obligatory!
With our reducers and sagas set up, it's time to create our store and that's defined in the
This way, it will become a pleasure to work with centralized state! Cheers! Time to move on :D
That works exactly the same way like the
components folder, but for the common styles across components or views, like your grid system for example!
components folder we defined our visual — or presentational — components, which rarely rely on state and/or another components orchestration. In the views folder, however, we define our container components, which will serve as, you guess: the views for our application. As you might think, these components most of the time rely on state, so no arrow functions here and extending from
React.PureComponent instead of
React.Component is recommended!
These views might have children components. And it's fine to declare them in the related view folder in order to avoid defining that much "global components". I usually move the component to the
components folder if I use it in two views or more.
index.js file of the views folder: most of the times I define it as the
Main component in my app, because it usually contains the app's
Done with the views! Time for our last file: the
Now, it should be obvious that the
Provider stuff is only needed if you want to bind Redux to your app. Just ignore if you don't. And there you have it: a very well organized folder structure and app! You should be able to render it in the DOM by calling
ReactDOM.render by now!
Before talking about the stack (a.k.a. libraries that I use and I would like you to use), let's talk about CSS.
The React's CSS war
You know, we know, that are tons of posts and libs to work with CSS in React. I like to work with CSS modules. It's CSS on steroids and I can use with SASS as well, so I really don't see the reason to switch for Styled Components and others. I'll not teach you how to work/install CSS modules in this article. But read this if you want to.
Buuuuuut… I've made something different. While I love to work with CSS modules, sometimes it just doesn't fit: for global styles of third-party components, for example. So, I'm working with modules, and still working with globally-imported styles with this configuration on my
webpack.config.js file Gist. Check it out!
I only work with SASS. Not interested in making it work with plain CSS, but it should be almost the same configuration. Now, in my components, I do it like that:
Ok, that's a contrived example, but it should show exactly what I'm saying, and using the amazing
Finally: the basic stack!
Now, there are some libraries that I like to use and which I found pretty helpful:
- Always the latest
react-dom, of course;
classnamesfor painless class names declaration;
colorfor color manipulation;
underscorefor a set of utility functions (that would possibly make our
underscoreif you're totally into pure functional programming and composition;
- If you liked
ramda, you're probably going to like
recomposeas well (good for composing components from pure functions);
dinero.jsfor money manipulation/display;
date-fnsfor date/time manipulation/display (I'd go for the latter because
momentis not pure and that just ain't good — thanks for the heads up, Marcelo Camargo);
react-virtualized, because sometimes one is just simpler than other and
react-tablefits well when you don't want to display a ridiculously big amount of rows. Also,
virtualizedlooks more like a
listrenderer than of a table;
redux-loggerfor centralized state. Those two middlewares play well together. A must!
react-router-domif you're building for the web, and
react-navigationif you're using React Native.
Now that should keep you up and running for a good large scale project! This article is pointed towards React DOM, but with some tweaks you should make it work with React Native!