Structuring React projects with CRUV

Learn about CRUV, a project structure that builds on create-react-app with 4 standard directories and 3 files. Focus on making your app amazing - not on where things go.

17th August, 2018

So you’re starting a React project. You know what you want to build, and quickly spin up a project with create-react-app. And then you’re confronted with the problem of where.

Where do your components go? Where should you put business logic? Where do higher order components fit in? And even if your structure feels right now, how do you know that it won’t feel wrong later?

If you’ve used other frameworks, you know that it doesn’t have to be this way. Rails gives you structure. So does Angular. What if React also had a system that told you exactly where files go? You could focus on building stuff instead of dicking around with folder structures.

In fact, there is a system that does just this. I’ve been using it for months now. It works great with create-react-app and any router you pick — even no router! And it’s named CRUV, after the 4 directories that you’ll use.

CRUV is inspired by the directory structure from Ruby on Rails. It gives you four standard directories, for the things that you’ll definitely use. Then it lets you add other app-specific folders and subdirectories once you need them.

Creating a CRUV app is simple. All you do is generate an app with create-react-app, and then create 4 folders and 2 files. You can try it out by copying and pasting this into your terminal:

But what do the 4 directories and 2 files in this structure actually do? Let me demonstrate with examples from the app that runs my new Raw React course. Shameless plug: join my newsletter below to be in the loop when the course is released, and get a massive discount.

A <LoginPage /> component that shows a login form, and handles user input on that form.

So what router should your routes use? Whichever routeryou’dlike, or even none at all! Because even if your app runs from a single URL, you’ll still need an <App> component. Maybe you’ll have other one-off components too.

Put components that render markup and styles here, along with components that receive and display user input. Dan Abramov calls them presentational components. I call them “views”, because it’s faster to type within import statements..

Some examples:

Styled <Button> components.

A <CourseCard> component that creates a link to a given page, and looks like a “card”.

A <Grid> component that is used within route components and other views to handle layout.

I tend to start by creating one .js, .css and .test.js file per component, and putting them directly in the /views directory. So for example, /views/Button.css, /views/Button.js, and /views/Button.test.js. But this isn’t a hard and fast rule; you can also export multiple components from a single file. For example, an Indicators.js file may export both <Spinner /> and <LoadingBar />.

Apps often have configuration that differs depending on the environment. For example, an API_KEY environment variable that differs between development, staging and production. While you can access this kind of configuration directly from process.env, I prefer to export it from a top-level config.js file. For example:

To use React’s Context API, you’ll frequently need to import <Context.Consumer /> components, which often don’t fit in any of the other categories. I like to keep these components in a single top-level file, making them easy to access, and also discouraging over-use of context. Here’s an example:

As your project grows, your views and routes folders can start to feel a little full. In this case, you might want to add subdirectories for each component, with an extra index.js file that re-exports the components. This let’s you keep the same filenames as before, without having to change any import statements.

For example, a /views/Button/index.js file might look like this:

export*from'./Button.js'

You may also find yourself adding subdirectories that group related components. For example, I have a /views/editor subdirectory that groups together all the components involved in my course’s live editor.

Finally, in addition to the standard four CRUV directories, you may also find other useful directories. For example, my app has a /types directory, because it’s written in TypeScript. But I didn’t start with this directory. I only added it once my /utils directory got a little heavy with type definitions.

The reason that the CRUV structure works is that it’s just a starting point. When you’re just starting out, it gives you a simple structure to work with so that you can concentrate on writing actual code. And once your folders get full, all you need to do is find a few similar files, and move them into a new directory. It’s easier to categories files that already exist than to predict the future.

You may noticed that I haven’t added a directory for Redux reducers. And that brings me to this article by one of the creators of Redux: You Might Not Need Redux. Yes, you read that correctly: one of the creators of Redux is asking you to reconsider using Redux.

The reason that there are no /reducers or /actions directories in the CRUV structure is that Redux is optional. In fact, I write apps without Redux all the time!

But what if you do use Redux? Or MobX? Or Govern? In that case, my recommendation is that you add a /store directory, and put all your store related code in there. Stores hold global state that isn’t tied to a specific component, so they generally shouldn’t very large.

And if you do have state that needs to be tied to a specific component? Use setState()! That’s what it is there for; component state is a simple, powerful, and underappreciated tool. Raw React can get you a long way.

If you’ve worked with some React apps before, CRUV may feel familiar. While the names are probably a little different, this pattern is something that often evolves by itself — especially when building on the Presentational and Container Components pattern.

In my case, CRUV is the structure that evolved from Frontend Armory itself. I’ll be going into more details on how I built this soon, so be sure to join as a member if you haven’t already!

Go Pro

Stay in the loop.

Keeping up to date with React is a full time job that pays only in frustration. Luckily, you can delegate! Just become a free member, and we'll keep you up to date with our monthly newsletter.