I have used the first approach in a medium sized project and it worked quite well. I recently tried out the second example in a new project (that only has 6 container components) and it didn’t quite feel right, so I’ll stick with the first option.

I see no problem with either approach.

Side note: I think there’s a fine art to recognising the point at which it makes sense to say “you know what, it doesn’t really matter” and moving on with your day. This article nails it and is a nice short read. (Warning: there’s a swear word in the title of that article, so don’t click the link if you’re offended by the word fuck.)

Self-contained components

My rule: if I have a project with more components than a Klein bottle has sides, I put each component in a directory along with a CSS file and a test. This rule is common for good reason, but even if you do have everything neatly tucked away in the same directory, you’re still quite free to make one huge mistake…

Take a look at your nearest file that contains a component. At the top of that file you will probably see a list of all other files that the component relies on as a neat set of imports.

Unless, that is, you share CSS classes between components. Then you also have a bunch of dependencies that aren’t listed.

Sure, you will save 7 seconds by using that .modal-wrapper class in your <DropDown> component because it already had the shadow you wanted, but oh boy do you even know the world of pain that you have just inflicted upon your future self...

[deleted scene: 14 paragraph rant about why this is a bad idea]

Trying to convince some people to not share CSS classes between components is like trying to convince people to “avoid using global variables in JavaScript” or “vaccinate your chickens” — some folks just aren’t gonna listen.

CSS-module users are no doubt wiggling their butts feeling pretty darn pleased with themselves right now. And they have good reason to be — they have a setup that can enforce the explicit import of CSS classes. If you care about keeping your CSS tightly coupled with your components, you should be using CSS modules too.

File naming

One rule that I find immensely useful is this:

name your file the same as the thing you’re exporting from that file

To some this is not even worth typing out it’s so obvious. But I’ve seen plenty of code where this isn’t the case and boy does it make it slow to move around.

(Don’t forget, all of this is personal. When I say “slow to move around”, it’s totally OK for you to think “it wouldn’t make it slow for me”, and therefore decide that there is no point in naming a file the same as its default export.)

Something I do very often is open a file by typing its name. If I have a utility function called toString then I fully expect that I will have a file called toString that I can open by typing exactly that. Something else that I do very often is switch tabs to a file that I already have open. For this I’ll expect that tab to have “toString.js” as it’s name.

Structures like the below make me want to curl up in a ball in a fireplace that is in use:

It staggers me that some people are happy to work like this.

Even if your IDE is clever and puts the directory in the tab name for non-unique filenames, you still have a bunch of redundancy there, and will run out of tab room sooner, and you still can’t type the filename to open it. I don’t even need to try this approach to know I won’t like it. Like my cousin’s new band, Mass Spectrometer that “don’t really fit into any known genre”. Get someone else to drive you to the gig.

Having said that, I understand that there is reasoning behind this — it means your import statement is this:

import Link from '../Link';

Instead of this:

import Link from '../Link/Link';

It’s a clear trade-off. Shorter import statements vs file names that match exports.

Let me just look this up… I import a module into another module, on average, 18 times a week. I open a file by typing its name roughly 840 times a week, and I look for a name on a tab roughly 1,892 times a week.

So I’ll take one extra (auto-completed) word in my import path, thanks.

Savvy readers will be shouting at their screens: there are two solutions to this that allow you have the file name match the thing being exported, and avoid having to type it twice in the import statement.

The first is to put an index.js file in every directory that exports a component, like so:

Since Node will look for an index.js file when resolving an import path, a path to ../Link is really a path to ../Link/index.js which is a file that points to the actual component file.

If typing fewer characters in your imports is important, then maybe an extra file for every directory sounds like good value. I think it’s a bad deal and I will repeat one more time that it’s seriously OK to have a different opinion on the matter.

The second ‘solution’ is this monstrosity:

By this point you will know that if Node doesn’t find ../Link/index.js it’s going to check to see if ../Link/package.json exists. If it does, it’s going to resolve to the value of the main property.

I think you have to really really hate typing one extra word in your import statements to go as far as creating a package.json file for every component. It’s just weird. And the more weird shit you put in your code, the weirder you become.

These two types of ‘redirecting’ files both mean that your import statement no longer points to the file where the thing is defined.

In the olden days, this would break the ability to ‘jump to source’ — something that is crucial to me being able to move throughout my code with ease and speed. WebStorm is clever and will resolve these hops (it will ‘know’ that I don’t want to jump into the index.js file, I want to jump all the way to the Link.js file), but if your text editor isn’t so clever, this might leave you with a lot of index.js files open, or jumping to source might not work at all.

So before subscribing to an approach, try it out and see if it’s going to be a hindrance to the way you work.

.js vs .jsx extensions

Until recently I’d always used .jsx extensions for any file that contained JSX, and used .js for anything that was vanilla JavaScript. This gave a clear distinction when opening/viewing files and as a bonus, you’ll get proper JSX syntax highlighting in GitHub.

However Facebook suggests not using the .jsx extension, so I’ve recently been using just .js and I’m glad I didn’t waste too much time weighing the pros and cons because it makes zero difference to me.

I recommend flipping a coin to decide.

Index files for utils

As a result of writing this article, and really thinking about what matters to me, I have actually made a change to one small aspect of my personal preference for app structure.

I used to create an index.js file for my utils, like so:

So that I could import multiple utilities in one go like this:

So neat!

Whenever I added a new util (0.8 times per week) I would simply add the util file and a new entry in the index file. Whenever I saw a PR that added a util and forgot to add it to index.js I would remind the developer to add that in. I would also occasionally discover utilities that weren’t in index.js , so would have to add them myself. Such an elegant solution.

For some reason it took until September 2017 for me to realise that this only added complexity. And that actually ditching the index.js and doing this was better:

It’s fewer lines, one less file, and one less thing to explain to new developers.

But those long import paths burn my eyes, so let’s take a look at a couple of solutions. There are two of them, as is often the case with couples.

Solution one is to use Webpack’s alias resolving to refer to my utils directory without a relative path. Here I’ve mapped Utils to src/app/utils ; the result is pretty, and aligns nicely with the way I import other utilities.

Capital ‘U’ by convention to differentiate it from npm packages

In the olden days this would confuse some text editors because they wouldn’t know what or where Utils/formatDate was. My IDE is clever and reads my Webpack config (it actually runs webpack under the hood), and makes the connection to the correct file (so I can jump to source, get autocomplete, etc).

(The theme continues: the tools we use and the way we use them should most definitely be taken into account when thinking about how to structure our apps.)

So… it’s a beautiful, neat, solution. But what’s behind the scenes?

This is a nice solution but it has two negative impacts.

It adds complexity. We now have more moving parts to achieve the exact same outcome. It reduces clarity. Someone unfamiliar with your Webpack config can’t look at an import statement and know what it’s referring to.

The second solution is to convince myself that I don’t care about the dots and leave them as they are.

To convince myself, I thought about how often I type out an import path, and as it turns out it’s exactly as often as I make coffee. Then I thought about how really, it’s not so hard to type eight dots and five slashes, compared to the time it takes to put the little coffee pod in the machine, measure out a teaspoon of sugar into a cup, milk the cow just a little bit (shout out to Daisy) and press the picture of a cup.

The trade-offs between these two options are representative of many different decisions (in life, and in programming), so this feels like a good time to debut my clarity/obscurity-simplicity/complexity matrix.