Next.js Project Structure

Yannick Wittwer
7 min readNov 12, 2020
https://unsplash.com/@ripato

Hey and welcome to my very first blog post on Medium. My name is Yannick and I’m a Junior React Developer for a company based in Bern Switzerland. During the last year, I learned a lot about React and the React Framework Next.js. In this blog post, I will present to you my default project structure for my Next.js projects. I would like to learn from you so feel free to comment your thoughts or improvements.

Folder Structure

This is the folder structure from the boilerplate I created. I will explain each directory and file below. Feel free to clone the boilerplate to have a starting point for your new Next.js project.

/.github
/workflows
build-and-test.yml
/app
/api
/[Name]API
[Name]API.js
[Name]API.test.js
RestClient.ts
/components
/elements
/[Name]
[Name].tsx
[Name].module.scss
[Name].test.ts
/modules
/templates
/layouts
/constants
/context
/[Name]Context
[Name]Provider.tsx
[Name]Reducer.ts
[Name]Actions.ts
/hooks
/styles
/abstracts
_functions.scss
_mixins.scss
_variables.scss
/base
_base.scss
_typography.scss
app.scss
/types
/utils
/test
/fixtures
/spec
/integration
/e2e
/support
/pages
404.tsx
_app.tsx
_document.tsx
_error.tsx
indext.tsx
/public
/images
robots.txt
favicon.ico

GitHub Workflows

In this folder, you will find all the workflows for GitHub actions. By default, I added one workflow.

build-and-test.yml
This workflow will run on every pull request creation, to make sure every test turns green and the app build runs successfully. You could also run it on every push but I recommend you only do this on your main/master and develop branch, to save GitHub Actions minutes. The GitHub Actions feature is billed by minutes/month. You can get more information in the official docs from GitHub.

app

The app directory is where our application actually lives. I like the approach of having the application files, which are not dedicated to the framework bundled in a specific directory.

api

This folder contains all code we need to access the APIs of our application. Personally, I like to have one folder for each REST API controller. Each folder then contains the functions for the API calls as well as the tests.

RestClient.ts
The RestClient contains basic functions like get, post, delete, and so on. Those functions are then used by the APIs. I like to use axios for this one.

components

The components directory contains all your elements, modules, templates, and layouts. I will explain each of these under a separate title. Some of you may prefer the Atomic Design by Brad Frost which is fine as well. In my opinion, the Atomic Design Pattern is sometimes a little bit confusing whether a component is a molecule or organism.

During my search for an alternative to the Atomic Design Pattern, I found this post from Dennis Reimann. I tried to pick the best out of these two approaches and created another one.

elements
This directory contains all the basic building blocks for your app. For example a button or a headline component.

modules
Create all your components here which are more than a basic building block. This could be a header or a footer component. Most likely those modules are built out of multiple elements.

templates
In the templates directory, you should place all your page templates which are then called from your Next.js specific pages. You can find an example of this pattern in the Repository.

layouts
Layouts are used to wrap your Templates and provide them with the components which will be displayed by default in a specific layout. For example, you would include the Footer and the Header in a default layout.

constants

Put all your global constants here. A good example would be your action types.

context

You only need this folder if you use the React Context API. If you use Redux, MobX, Recoil, or any other state management library you will most likely have different folders.

I recommend you create one folder per context. What I like to do then is to separate the different parts of my context in my own files to keep everything clean. In the Provider.tsx I create my context as well as the provider component which takes children as a prop and wraps them with the context.

import testReducer from './TestReducer'const TestContext = React.createContext();const TestProvider = (props) => {
const [state, dispatch] = React.useReducer(testReducer, {
isTest: props.isTrue
});
const value = [state, dispatch];
return <TestContext.Provider value={value} {...props} />;
}
export default TestProvider;

Then I like to separate my reducer to his own file. Because based on my experience some reducers can grow quite big depending on your application. So I think it’s nice to have this reducer function separated.

export const testReducer = (state, { type }) => {
switch (type) {
case 'TOGGLE':
return { isTest: !state.isTest };
default:
throw new Error(`Unsupported type: ${type}`)
}
};

Last but not least the actions. For every action that can be dispatched I like to create an own function which takes the dispatch function as well as a possible payload as an argument.

export const toggle = dispatch => dispatch({ type: 'TOGGLE' });

You can get more information about the Context API in the official docs.

styles

The styles directory is an addition to the scss files you have in each component directory. I like to place all my global styles like variables or utilities in this directory.

You may use a different approach for your application styling, so feel free to customize or remove this directory.

types

If you use typescript, which I strongly recommend, put all of your types that do not belong to a component into this folder. I also recommend you to create subfolders to bundle the types which belong together. If you need an Introduction to React + Typescript I think this documentation may help you.

utils

Most likely you will have some JS functions which you will use over and over again. I would recommend you to outsource them into separate files.

test

The test directory is where I like to place my integration as well as my system integration (also called e2e) tests. For those tests, I like to use Cypress. Feel free to test it out and use it in your project.

The spec subdirectory is used to bundle all the integration- and e2e-test specifications. The fixture subdirectory is used to place the mock data for the integration tests.

If you need to write your own cypress commands you can place them in the support directory.

If you would like to learn more about how I write integration and e2e tests, let me know in the comments then I will write a separate post.

pages

All the routes of your Next.js application will be placed in this directory. For each route, you will have a separate file, which is named as the route. So for example the file about.tsx in the pages directory would create the following route: https://whateverurl.com/about. You will get more information about the routing in the official documentation from Next.js.

By default, I added five pages to the directory. The 404.tsx is most likely self-explaining. To display a custom error page in case of a 5XX HTTP error you can use the _error.tsx file. The index.tsx is basically the homepage of your app.

For the custom document (_document.tsx) as well as the custom app (_app.tsx) I would recommend you to read the official Next.js docs.

public

Next.js uses this directory to statically serve files like the robots.txt or the favicon.ico. You will get more information on how to include these files in the official docs.

Thanks for your valuable time. I hope you have learned something new or at least got some inspiration for your next project.

--

--