Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lancejpollard/8ed150c82af70ff6ddf6f3d1da7b0f26 to your computer and use it in GitHub Desktop.
Save lancejpollard/8ed150c82af70ff6ddf6f3d1da7b0f26 to your computer and use it in GitHub Desktop.
Folder Structure for Modern Frontend JavaScript Apps

I have thought about folder structures for many years. What I've found is that there is no standard way to represent all things, you have to decide on something and either stick with it, or adopt an evolutionary development mindset (EDM). An EDM is one where you're free to test out new ideas and your code (or in this case, folder structure) is free to evolve into a better and better form as you uncover the mysteries of what is best in your case. You let your team create whatever folder structures they like, without enforcing it, and see what sticks. This will lead to variations in style, but you can then go through and change it again to be even better. Stuff like that.

What I've decided on for the frontend is something like this.

action

That's it! Now, everything in code in reality is an action if you boil it down. That is what assembly is if you take a look, just a bunch of actions. Every action can be composed into more and more complex actions. Every language supports actions (called functions in most places). Essentially, you have a folder of functions. This works in the same way across languages.

But then, you have refinements on the action concept. Specific cases of actions. This is, essentially, DSLs (Domain Specific Languages). Common DSLs you use in programming are for defining things such as:

  • routes (as in Rails routes)
  • ui components (as in React components)
  • grammars (as in PEG grammars)
  • HTTP calls
  • constants (text)
  • tests
  • hook (react hooks)
  • validation (for form fields)
  • masks (for regexes on inputs)

For each DSL you have, where you create several objects with this DSL, you can have a folder named for it. So in a React app case we might have these (also note, I name them all in the singular, because then you don't have to do any string transformation when doing dynamic matching based on the string, and it just fits better if you know what I'm talking about):

component
route
grammar
constant
test
request
action
hook
validation
mask
keyboard

Everything that doesn't have a DSL can be defined as an action and should be put into actions until you invent a DSL for it.

If you would like, you can nest the DSLs related to each higher level thing, such as components having their own scoped actions and hooks, etc..

component/
  hook/
    index.js
  action/
    index.js

Now, what about the name of files and whether something should be a folder or a file? You gain normalization and consistency if you name every file the same way, that is like index.js for example. Rather than having files like hooks.js and Button.js. With taht you have both folders and files with arbitrary names. With normalized file names, you have less diversity in the number of custom structured files. Files all follow the same pattern, so that is waht I recommend.

This is the most practical folder structure for every case I have encountered. Putting DSLs into their own spots makes it so they are organized and easy to reason about. It establishes a standard convention as well, and can grow with any new DSL you invent.

So now you have organizing based on different scopes, not necessarily of actions. You might group things related to the frontend or backend, or related to specific subprojects or subsubprojects, etc. Or for different organizations within the same repo! All of this can be done if you put a readme at each point, and put the code in that point.

myorg/
  readme.md
  org/
    mysuborg/
      readme.md
      code/
        client/
          component/
            App/
              index.js
              index.css
        server/
          index.js
    mysuborg2/
      readme.md
      code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment