Micro-app folder/file hierarchy
The best way to organize your (front end) source code
I’ve seen a lot of different ways of organizing source code files and folders over the past two decades or so. Along the way, I picked up a few patterns that have worked very well for me. I continue to improve the system, but, frankly, the improvements have become less frequent and smaller.
I think I might be nearing perfection! Heh.
The file is named “index”
One thing that I adopted very early on—so early that it was back when all my files ended with
.html—is to name the default file in the folder “
The immediate benefit of this back in the day, and I can’t believe I’m admitting this publicly, is that if I wanted to add superpowers to the file, I could simply rename
index.html to, cough, cough,
index.php. And it just worked.
We can just pretend that I never said that. But it worked just as well later when it became
index.rb and then
index.scala and finally
Wait, what? Then how does one name the components if every file is called
The name of the component, utility function, whatever goes on the folder, naturally:
App/ index.ts index.module.css index.test.ts
Benefit one: none of this nonsense:
import App from "./App/App"
import App from "./App"
The other great benefit of this is that we can colocate all the files that relate to that component/function in the same folder.
That means we don’t have a separate
__tests__ folder somewhere far from our component. Or a
It means that I can tell at a glance if this component has CSS associated with it, is tested, has Storybook stories, etc. Because the files are right there.
It’s all right there in the folder!
So that’s the key to all this:
Keep things that belong together… together. Who knew?
Everyone wants to belong
Another benefit of this is that I can close up those folders, so instead of seeing this monstrosity:
We can has this:
And that works all the way down:
Mirror the application structure
It’s the application, stupid.
If your interface is represented by components, and those components are nested, then why wouldn’t you nest them in your source code folder hierarchy?
Consider this: if the
Logo, and a given
Nav component appear only in the Header, then shouldn’t their code be nested there as well?
If I moved the
Nav component up to be a sibling of
Header and did the same in with the footer’s
Nav, then I’d have to rename them
FooterNav. It’s just more noise. Nesting them makes it clear.
Sharing components or functions
But what if it’s the same
Nav component? Maybe we’ve abstracted it.
OK, then, we only need one, but where does it go?
In this instance, the folder for that component or function should be raised to the node in the tree where the branches that use the component meet. So in this instance, if we were going to make those two
Nav components into one, we’d take this:
And turn it into this:
Nav component is used by both
Header. These two branches meet at the
App node, so the
shared folder goes directly beneath the
If, for example,
Social shared a component, then it would go in a
shared folder directly under the
Footer folder, as that’s the lowest point still above the two branches where the component is used.
Social shared a component, then that component would have to go in the
shared folder under
App, because it is used in both the
Header branches and
App is where they meet.
It’s pretty simple. And now whenever you see a
shared folder, you know:
The items in this folder are used in more than one place.
Wherever those places are, they are guaranteed to be below the
sharedfolder’s parent folder. That is, they are in siblings of the
I generally use
shared folders for shared components. For utility functions, I have a separate shared folder called
utilities. This separation of concerns improves readability in the shared folders. I’m not visually sorting through functions and components trying to find what I want.
Which brings up a thumb rule: If the folder name is PascalCase, then it probably belongs in the
shared folder; if the folder name is camelCase, then it probably belongs in the
Utility functions follow the same principle as shared components: they go as low in the folder hierarchy as they can. Which means that if a utility function is used by only one component (or other utility function), then it belongs in the folder of that component:
utilities folder under main holds utilities used only by
Main and maybe below. If a utility in this folder is needed in, for example, the
Header, then that utility’s folder would be moved into a
utilities folder immediately under
There is almost always a significant number of utility functions that I use pretty much everywhere. If they are not specific to this application, I generally put them into a top-level folder, sibling to
I’m not a fan of adding dependencies willy-nilly. So rather than add, say, Ramda or Lodash (I prefer the former), I simply write my own utilities. It’s not difficult, and then I know not only that they work, but how they work. If it’s something difficult or there is a performance issue, then OK, maybe it’s dependency time, e.g., I’m not writing my own version of React… or am I?
Thus functions such as
But the point of this top-level utilities folder is that I could easily extract it into an npm or Deno module. I’m particularly fond of Deno modules these days. Nothing in this
utilities folder is specific to my app. Nothing.
Ditto for the
services folder. These are generic services my app uses. Nothing specific to my app lives in the
services folder. I inject that information where I use the service in my app (in the
That means that if I’m using a specific URL to connect to the database (isn’t everyone?), then that URL will be assigned to a constant somewhere in a
constants.ts file, and that file will be in a
utilities folder at the lowest point practicable in the App hierarchy, where everything specific to this app belongs.
As with the top-level
utilities folder, each of these services could be extracted into an npm or Deno module and loaded from the net. Generally, that’s the plan, and they are only colocated here during development.
One benefit of Deno is that we can load them directly from, say, GitHub while keeping them in this folder for easy reference. But then I guess we could also publish them to npm directly from here. It’s just a bit messier.
What are the benefits of this style?
Other than clarity and reduced cognitive load, which are enough to justify the price of admission in my book, the self-contained component/app/function lends itself to easy movement, duplication, or deletion. Consider this:
If I decide to delete the
Sidebar, I just delete its folder. I don’t need to hunt around for tests, stories, CSS specific to the component, or subcomponents. I just delete the
Sidebar folder and delete any references to the
Sidebar, which are almost certainly only in the
So easy and clean!
And what if I decide that the Footer and the Header should really be subcomponents of Main? I just drag the folders in there and I’m probably done:
Again, easy as can be. And everything travelled with these components: their authentication, their connections to the database or to some kind of state, other service connections such as
Or maybe I want to make the
Sidebar a sibling of
Hard to beat. Questions? I’ll probably touch on this again soon, maybe when I discuss how I split code up into files and use the module system for easy and clean reuse.