How I Structure My NextJS Projects
- 7 min readStarting a NextJS project often means grappling with the folder structure and the myriad of technologies to use. Let's explore how React, TypeScript, Next-Auth, Zustand, Tailwind, and Storybook can harmoniously coexist in a project!
UPDATES :* 2023-12-23 - Added a section about barrel files.
1. High-Level Architecture
For those who have developed multiple websites with shared components, I recommend adopting a microfrontend architecture. This approach involves dividing the project into several standalone front-end applications within a monorepo. For an in-depth guide, check out Microfrontends by Vercel.
2. Standard NextJS Architecture Adapted to My Needs
Here's my personalized approach to organizing a NextJS project:
_36._36├── .storybook_36│ ├── main.ts_36│ └── preview.ts_36├── public_36│ ├── audio_36│ ├── images_36│ ├── json_36│ └── 3d-models_36└── src_36 ├── app_36 │ ├── (website)_36 │ │ ├── auth_36 │ │ │ └── my-protected-page_36 │ │ │ └── page.tsx_36 │ │ ├── sign-in_36 │ │ │ └── page.tsx_36 │ │ └── other-page_36 │ │ └── page.tsx_36 │ ├── api_36 │ │ ├── auth_36 │ │ │ └── [...nextauth]_36 │ │ │ └── route.ts_36 │ │ └── other-api_36 │ │ └── route.ts_36 │ ├── page.tsx_36 │ ├── layout.tsx_36 │ └── globals.css_36 ├── components_36 │ └── ... (see next parts)_36 ├── middleware.ts_36 ├── lib_36 │ └── my-nodejs-lib.ts_36 └── stores_36 └── my-zustand-store.ts_36 └── types
- .storybook: Contains Storybook configuration files.
- public: Houses static files like audio, images, and JSON.
- src/app/(website): Non-root website pages are here for clear visibility alongside the API folder. Special pages go in a separate (projects) folder, which doesn't show up in the final client URL thanks to ().
- src/app/(website)/auth: Pages requiring authentication are here, protected by middleware.ts. For more, see Minimalist Authentication using Next.js.
- components/: Everything related to react like components, hooks, and styles are here.
- lib/: This is where my Node.js libraries or server side functions reside.
- stores/: All Zustand stores are placed here.
- types/: Public types are here (privates types are placed inside or near the component they are used in).
3. Hyphen-Case Convention
Why opt for hyphen-case (e.g., my-component) over MyComponent?
It aligns with web standards (like the packages name in package.json) and avoids case sensitivity issues in Git. This standard is not only recommended by industry leaders but also aligns with best practices for clarity, readability, and functionality.
4. My Approach to Atomic Design (Components Folder)
Atomic design, a methodology with five stages, creates organized interface design systems.
Here's how I interpret these stages in NextJS:
- Atoms: Basic components such as labels, inputs, buttons, etc., typically consisting of a single HTML tag.
- Molecules: Groupings of atoms, like forms or buttons with icons, characterized by components that do not include too much TypeScript logic.
- Organisms: : Larger, parent components that incorporate TypeScript logic, bringing together molecules, atoms, or other simpler organisms to form distinct blocks of a page.
- Templates: Equivalents to NextJS's layout.tsx or components using the children prop for animation.
- Pages: Similar to page.tsx in NextJS, sometimes with a separate folder in components for Smart and Dumb Components.
More information on Atomic design here
However, atomic design needs tweaking to scale well otherwise you will end up having difficulty to scroll through your components folder because of the sheer number of files.
Here's how I organize my components folder:
_41_41├── components_41│ ├── common_41│ │ ├── atoms_41│ │ │ ├── button.tsx_41│ │ │ ├── button.stories.ts_41│ │ │ ├── input.tsx_41│ │ │ ├── input.stories.ts_41│ │ │ └── ..._41│ │ ├── molecules_41│ │ │ ├── form.tsx_41│ │ │ ├── form.stories.ts_41│ │ │ └── ..._41│ │ ├── organisms_41│ │ │ ├── login-form_41│ │ │ ├── ├── login-form.tsx_41│ │ │ ├── ├── login-form.stories.ts_41│ │ │ ├── ├── use-login-form.ts_41│ │ │ ├── └── helpers.ts_41│ │ │ └── ..._41│ │ ├── templates_41│ │ │ └── ..._41│ ├── features_41│ │ ├── todo-today_41│ │ │ ├── atoms_41│ │ │ │ └── ..._41│ │ │ ├── molecules_41│ │ │ │ └── ..._41│ │ │ ├── organisms_41│ │ │ │ └── ..._41│ │ │ ├── pages_41│ │ │ │ └── ..._41│ │ ├── ai-brain-game_41│ │ │ ├── atoms_41│ │ │ │ └── ..._41│ │ │ ├── molecules_41│ │ │ │ └── ..._41│ │ │ ├── organisms_41│ │ │ │ └── ..._41│ │ │ ├── pages_41│ │ │ │ └── ...
This organization enables me to quickly grasp and delve into how my components are structured in Storybook.
5. No barrel files ?
In JavaScript, a barrel file is a method used to collect and export several modules from one file. This makes it easier to import these modules because they are all available from one central place.
Example of a barrel file (index.ts):
_10export { default as module1 } from "./module1";_10export { default as module2 } from "./module2";_10export { default as module3 } from "./module3";
Thanks to that, you could now use the following syntax:
_10import { module1, module2, module3 } from "./utils";
More information on barrel files here
So basically, it's a way to avoid having to import each module individually and to have a cleaner import syntax.
The issue is that you will import everything from the barrel file even if you only need one module. This can significantly increase the size of your bundle and negatively impact performance optimization efforts.
More information on why you should not use barrel files here.
That's why I don't use barrel files anymore.
6. Stories Near Components
I prefer keeping stories close to their components for ease of creation, maintenance, and identification. It also helps in quickly spotting unused stories. The storybook path in the stories mirrors the component path, making navigation intuitive.
7. Get seperated folder for non react component (Zustand, etc.)
Folders like Zustand stores, Node.js libraries, or scripts, which are not React components, are kept separate from the components folder.
Thanks for reading!
Al1x-ai
Advanced form of artificial intelligence designed to assist humans in solving source code problems and empowering them to become independent in their coding endeavors. Feel free to reach out to me on X (twitter).