Hacker News new | past | comments | ask | show | jobs | submit login
How to Structure React Projects (reactjsnews.com)
226 points by zackify on Nov 29, 2015 | hide | past | web | favorite | 43 comments

Organize directories per-feature. What is a feature? Think of it as organizing by end-user value delivered. For example, "appointments/", with subdirectories "appointments/recurring/" and "appointments/import/" for those two really complex bits.

Don't make sub-directories by kind (screens/, controllers/, directives/, views/, styles/, etc), instead encode the kind in the filename (make it a suffix).

Don't end up with a system where you end up often making sub-directories that contain only one file. That's likely an unnecessary level.

The rationale is as follows: your system will never have a fixed number of components, but it quite likely will have a fixed number of "kinds of components." So as your system grows, you will have to group files by feature to deal with complexity. When this inevitably happens, it helps not to have a per-kind directory structure to swim against.

By naming your files with name of concern as a prefix and kind as a suffix, you also get related files together by the virtue of sorting files alphabetically in the same folder (monocline grouping).

I don't get the benefit of this approach -- it's too subjective. Is reset-password part of auth or settings feature? What is the link between routes and features? If I'm on a page and see a bug, how do I quickly find the code behind it if it's not linked to the route hierarchy?

The easiest mental model for me is to just map files/folders directly to the route hierarchy. That way you can rapidly build out the overall navigation scheme while building functionality. There are very few subjective decisions besides the route name. New developers can immediately grasp how things are organized and where things belong by interacting with the actual site. If they see a bug, or somebody suggests an improvement on a specific page, they can immediately find the code they'll need to modify.

With features you have to say something like: "Find routes.js, find the route forgot-password within react-router component, see what component it references, now go to top of the file and see the location for that components import. If it's a folder with an index.js file, look in that file and see what component is actually being exported, once you find the underlying component, open that file and try to find the bug."

The reality of business is that it's subjective. That's what we're paid for - to exercise judgment, and make decisions where there isn't a clear right or wrong answer. Anything that's objective can & should be automated. (This also gives a good rule-of-thumb for when & how structure should be imposed on projects: do so when it lets you replace human work with computer work. This is the idea behind convention-over-configuration frameworks.)

Anyway, the practical benefit of a feature-first hierarchy is that it makes the changes you need to add a new feature local to a single directory. This means developers don't need to constantly switch between directories in their terminals & file explorer, it lets you implement mechanisms for automated OWNERS-checking or code review, and it means that commit logs will instantly show which feature they touched just from the filenames.

In practice, "feature" is defined by the org chart: it's one concrete improvement that a specific team is working on to benefit the user.

If you're worried about finding the code for a feature on the screen, add a system where if you hover over any component in the UI, it pops up an overlay with a link to the source code. At a previous workplace, an engineer built such a feature in a couple days and it was an absolute lifesaver.

You have to make subjective decisions to structure your code anyway. So I feel that you will hit auth vs settings debate when you implement reset password anyway.

For something like reset password, put it wherever your team will find it. It feels more like auth to me, but if you have most of your auth functionality already built in your settings area, it might not make sense to move it (until it gets unwieldy and "uncomfortable" to keep it there).

Follow the principle of least surprise: if a fellow developer is looking for "reset password," are "settings" and "auth" the only two places where they would look? That's actually good enough. Think about all the times you really had to hunt around to find something that was placed in an unexpected, "weird" place.

>I don't get the benefit of this approach -- it's too subjective. Is reset-password part of auth or settings feature?

Just pick one or the other and be done with it. I'd place it at "auth".

>The easiest mental model for me is to just map files/folders directly to the route hierarchy.

Only files don't correspond 1-1 to the route hierarchy either. The same components can be used in 20 different routes.

This is where you would also benefit from something like TypeScript. You could just find any use of the component and "go to definition". Or use "jump to class".

I like this approach because it can be applied to any technology stack. It's also very refactor friendly.

Organizing by feature gives you immediate feedback about 'what' the application does, instead of what framework it's written in.

I find this structure much more useful during the application's maintenance lifetime, but it does force you to think more about naming and the division of what a 'feature' is.

See also from Uncle Bob: https://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Arc...

I’ve always liked organizing Rails projects along these lines. If I have a `foobar.controller.rb` controller, a`foobar.rb` model, and a `foobar.haml` view, why are they distributed in folders by “type?” It’s so much easier to find them in a related folder by default.

Mind you, that goes against the community “standard,” and we all know how that argument ends up.

Problem is when one component is used in two or more places. Then it goes in the dreaded "common" directory - a roach motel where files check in but never leave. Also the boundary for what a "feature" is, is likely fuzzy. I haven't seen a large project yet that successfully addresses these issues.

Recognize it and rock it. Meaning, don't be afraid to refactor it and take advantage of it.

Of course you will end up having common folders, the trick is to have more than one and at the appropriate level. Sometimes you need a common folder between a few close-but-not-same components. Sometimes you end up deciding to reuse that common folder elsewhere, so you "promote" it up a few levels.

Other names such as "shared" or even "service" can be used, as long as you put big related pieces in subfolders underneath.

Recognize when things get messy with common folders and refactor then. This is the "trick", so to speak. You have to recognize "our common folders are starting to become a mess" as valid technical debt, because the team will keep having a progressively harder time finding the correct code.

Just the other day I started with https://github.com/fortruce/react-fullstack-skeleton/ as a minimal skeleton for a new project of mine. Having not developed with "modern" Javascript yet, and only having used React by itself this was quite a convenient way to get started. Most other skeleton projects were too much, too huge for my taste when getting started.

It has:

* react-hot-loader which causes your changed to get automatically loaded into the running webpage

* Webpack, gulp and redux all set up

* A sample api server integrated in the development process to be used with a SPA

* SASS/CSS loading, which also gets hot loaded into your page (once/if my pull request is merged)

* Production builds with minifying and no sourcemaps (once/if my pull request is merged)

I'm not entirely happy with the structuring, but that can always be changed once you're familiar with the technologies. I also just submitted pull requests to address the issues listed in the readme.

That's a nice setup. Only thing that is incompatible with our react redux setup is thunk. We use redux-promise to work with async/await from babel stage 0.

I have been using this:


It's a little heavy, but it includes react-redux bindings, webpack, Babel for ES6 support, mocha for testing, SASS/LESS for styles. There's more info on the GitHub page but it seems to be pretty comprehensive in terms of bundling next-gen front end engineering frameworks.

I have been using the same thing. I find the distinction between containers (aka smart components) and components pretty neat.

The Redux Devtools project has an example folder with already structured examples [1]. Cool thing is that it comes with all the webpack hot-reloading already setup, and with the ability to do undo/redo changes thanks to the devtools capability. In essence, it's the only project I found that comes with the ability to code your entire app without ever having to reload your browser, and/or having to reproduce the actions manually. When you find a bug, you go and fix it, and magically everything goes back to the same state you were into without you having to click through the various buttons/panels/whatever that brought you there.

1. https://github.com/gaearon/redux-devtools/tree/master/exampl...

The samples provided by Facebook (for Flux) have a great example structure to them: https://github.com/facebook/flux/tree/master/examples/flux-t...

In my experience, this structure scales very well, especially if you take the advice in the 'thinking in react' tutorial (http://facebook.github.io/react/docs/thinking-in-react.html ) and create a mockup that clearly indicates what part of the UI will be handled with what component.

This is great!

One of the most confusing things with React and Angular or in fact any other non convention-over-configuration framework is how you go about building your project.

Structure is super important, and this resource looks good.

I would add a demo application on Github that does handle data (something super simple will suffice), just so people can fork the project and take it from there.

I know for me, when I started with Angular, the Yeoman generators were just life savers, I think it would be a great "next stage" for this post.

Our post on building your first redux app has a github repo with the example code. It is a pretty basic structure. http://reactjsnews.com/your-first-redux-app/

Agreed, structure is very important, and resources like this help codify that structure. One of the best aspects of frameworks like rails is a rock-solid project structure. New engineers so rarely have to ask questions about where some particular resource is located.

I've used a structure similar to the one suggested by Ryan Florence in this Gist [1] with some success.

The gist of it is:

> The file structure maps directly to the route hierarchy, which maps directly to the UI hierarchy.

> It's inverted from the model that we've used in other systems. If we consider all folders being either a "generic" or a "feature" folder, we only have one "feature" folder but many "generic" folders.

[1] https://gist.github.com/ryanflorence/daafb1e3cb8ad740b346

So basically, the default behaviour of ember.

My preferred boilerplate is [1], [2] is also pretty good. I think that the major issue is not React itself, but a bare minimum set of modules already pre-configured to work together, that for me are react-router, react-bootstrap, either flux or parse.

[1] https://github.com/MadeInHaus/react-flux-gulp-starter [2] https://github.com/kriasoft/react-starter-kit

I like the idea, as this is something we struggled with when we rebuilt Expedia's "Viewfinder" client-side app in universal React (SEO being the main reason for the project, despite being a client-side web app...).

One thing that becomes interesting though, is that all of the advice in the comments here saying "Follow the router!" works brilliantly if you've got a website-style app that you need to build. For us, the router is rather confusing, and all params are optional; it's more of a filter than anything else!

Because of that, we followed the more "convetional" (heh) tree: top level src/ folder, server/, client/, shared/ underneath it. Server and client only hold their respective entry points, and a few modules for the server-side behaviour (middleware and so on).

Under shared/ we have components/ which are separated into folders based on grouped functionality. Like so: https://i.imgur.com/9T0AX52.png

While that's slightly subjective, it worked in our case very well, and at the end of the day solid naming of your components means CtrlP becomes a lot more useful ;)

This is great but it really only applies to SPAs. I know single pagers are all the rage these days but it often makes a lot of sense to structure your application in a hybrid way where you have full page reloads for different sections of a site and each section is a container for a different SPA. I've got a decent setup for this but I'll be damned if I can find anyone who shares their setup for this sort of use case.

> I've got a decent setup for this but I'll be damned if I can find anyone who shares their setup for this sort of use case.

Could you release your setup somewhere?

Does anyone use React with RoR? How do you combine them?

I know of these two projects that offer some guidance to combining RoR with React:



Basically, you introduce webpack as part of your dev/build pipeline, it interacts nicely with the asset pipeline.

My current preference is the 'browserify-rails' gem. I set up a package.json file to manage my javascript, but I let the regular asset pipeline handle the css and images. I tried webpack for a while, but it seemed redundant when I already had the asset pipeline.

I also like to add 'babelify' to my package.json and 'config.browserify_rails.commandline_options = "-t babelify"' to my application.rb to get ES6 goodness.

I'm not in tune with the whole web thing, but nothing says overbearing framework like "structure guides" and scaffolding tools.

I'm particularly in awe because I just visited the ReactJS site to find out what all the fuzz is about and it makes the bold claim "just the UI", and "the V in MVC".

This is not necessarily a React thing. When using React, or most front end frameworks, none of them care about how you structure your project. This is more about how to structure a project that starts getting really big. I've done it. Well over 100 views in a spa, with hundreds of components...your structure must evolve with the project. At some point you will probably need a dumb components and smart components separation.

React itself is very, very simple. The ecosystem around it isn't but it's getting better.

People impose structure because they like it. You can have a React app in a single JavaScript just fine—it's what I prefer. Incremental search is nicer than jumping between a hundred tiny files in fifty subdirectories in my opinion, but to each their own.

So I use mocha and eslint, how do I configure those tools to allow test files in the same directories as the source?

The sticking point I found is enabling different lint rule for test and source in a way that doesn't require linting twice and that is automatically understood by my editor (vim + syntastic)

How is a "component" different from a "view"?

Per my understanding, a component is meant to be a re-usable unit which can be included in one or more views. A View can be thought as a single 'page' of the application, (eg: Login, Home, etc) which may be composed of several components.

One common interpretation:

Components are "dumb", and just consume props. Views are top level Components, usually rendered by the router, that tie into your fluxy goodness.

A component is is meant to be re-used in different contexts, a view not necessarily.

A view is made up of many components.

While the “views are many components” reply is true that’s not really the distinction. Views are React components that have state or other complexity to them. Components are React components that just consume props and return DOM that may reference those props but nothing else in your app (they won't look at state, globals, etc.)

Most of your components should be made up of smaller components. I'm not sure why this distinction is useful?

I see view (or screen) as something that maps to a route. It's true it's definitely a component.

I feel adding vocabulary like this to a system can help with understandability. That's the point I made with a "feature". It's something that could fit between views and components.

Atomic design (http://patternlab.io/) is close to this idea.

I've never done it this way, I keep calling my container components, components. In my projects I do not make a distinction and call them views. I was just stating the author's point.

Registration is open for Startup School 2019. Classes start July 22nd.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact