---
tags: Setup
---
# Application setup with Next.js and a robust configuration
Learn how to set up a Next.js application with useful tooling and a robust configuration. Enforce consistent standards, automate quality checks, and manage environment settings, creating a strong foundation for efficient development and seamless collaboration.
## Create a Well-Organized Workspace
I suggest structuring your workspace to mirror the application's domain and subdomains, grouping applications for easier management. For instance, `next-stack/my-app` would contain all files needed to develop and deploy the application hosted on `my-app.next-stack.com`:
```bash
mkdir next-stack && cd $_
```
## Create a Next.js Application
Run the following command and select:
- `my-app` as the name
- `No` for customize the import alias
```bash
npx create-next-app@latest --use-pnpm --typescript --eslint --tailwind --src-dir --app --turbopack
```
### tsconfig Configuration
Add the following properties to `compilerOptions` field in `tsconfig.json` file:
```json
"removeComments": true,
"forceConsistentCasingInFileNames": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"allowUnusedLabels": false,
"allowUnreachableCode": false,
```
### Add [EditorConfig](https://editorconfig.org)
Create a `.editorconfig` file:
```bash
touch .editorconfig
```
Insert the following content:
```
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
```
### Add [Vitest](https://vitest.dev/)
```bash
pnpm i -D vitest @vitejs/plugin-react jsdom @vitest/coverage-v8
```
Create the `vitest.config.mts` file:
```bash
touch vitest.config.mts
```
Insert the following content:
```typescript
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
},
})
```
Add the `test` command to the `scripts` property in the `package.json` file:
```json
"test": "vitest",
```
### Add [Prettier](https://prettier.io/)
```bash
pnpm i -D prettier prettier-plugin-tailwindcss
```
Create the `.prettierrc` file:
```bash
touch .prettierrc
```
Insert the following configuration:
```json
{
"plugins": ["prettier-plugin-tailwindcss"],
"printWidth": 128,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"quoteProps": "as-needed",
"jsxSingleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"bracketSameLine": true,
"arrowParens": "always",
"requirePragma": false,
"insertPragma": false,
"proseWrap": "preserve",
"htmlWhitespaceSensitivity": "ignore",
"vueIndentScriptAndStyle": false,
"endOfLine": "lf",
"embeddedLanguageFormatting": "auto"
}
```
If you are using WebStorm, when opening this file, a message will prompt you to use this configuration for the entire project. Select "Yes" to apply it.
Create the `.prettierignore` file:
```bash
touch .prettierignore
```
Ignore the following files and directories in the Prettier check:
```
.git/
.idea/
.vscode/
node_modules/
.next/
build/
dist/
coverage/
out/
```
Run Prettier on all files in the project:
```bash
pnpm exec prettier . --write
```
The files that appear in white have been modified, while those in grey were already properly formatted.
If you are using WebStorm, in the configurations, search for `Prettier`, select `Automatic Prettier configuration`, and check the box for `Run on save`.

Add the `prettier` and `prettier.ci` commands to the `scripts` property in the `package.json` file:
```json
"prettier": "prettier --write ./src/",
"prettier.ci": "prettier --check ./src/",
```
### Add [Stylelint](https://stylelint.io/)
```bash
pnpm i -D stylelint stylelint-config-standard stylelint-config-tailwindcss
```
Create the `.stylelintrc` file:
```bash
touch .stylelintrc
```
Insert the following configuration:
```json
{
"extends": ["stylelint-config-recommended", "stylelint-config-tailwindcss"]
}
```
Run Stylelint with the `fix` option on all `css` files in the project:
```bash
pnpm exec stylelint "**/*.css" --fix
```
Add the `lint.style` command to the `scripts` property in the `package.json` file:
```json
"lint.style": "stylelint \"**/*.css\"",
```
### Configurer [ESLint](https://eslint.org/)
Install the default ESLint configurations for JS and TS:
```bash
pnpm i -D eslint @eslint/js @types/eslint__js typescript-eslint eslint-plugin-functional eslint-plugin-import eslint-config-prettier eslint-plugin-security eslint-plugin-react eslint-plugin-boundaries @vitest/eslint-plugin
```
Replace the `.eslintrc.json` file with `eslint.config.mjs`:
```bash
mv .eslintrc.json eslint.config.mjs
```
Replace the file content with:
```js
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import functional from 'eslint-plugin-functional';
import importPlugin from 'eslint-plugin-import';
import eslintConfigPrettier from 'eslint-config-prettier';
import pluginSecurity from 'eslint-plugin-security';
import react from 'eslint-plugin-react';
import boundaries from 'eslint-plugin-boundaries';
import vitest from '@vitest/eslint-plugin';
export default tseslint.config(
{
files: ['src/**/*.spec.ts', 'src/**/*.test.ts'],
plugins: {
vitest
},
extends: [vitest.configs.recommended],
rules: {
...vitest.configs.recommended.rules
}
},
{
files: ['src/**/*.ts'],
ignores: ['src/**/*.spec.ts', 'src/**/*.test.ts'],
plugins: {
boundaries
},
extends: [
functional.configs.externalTypeScriptRecommended,
functional.configs.recommended,
functional.configs.stylistic,
],
},
{
files: ['src/**/*.ts'],
plugins: {
boundaries
},
extends: [
eslint.configs.recommended,
...tseslint.configs.strictTypeChecked,
...tseslint.configs.stylisticTypeChecked,
importPlugin.flatConfigs.recommended,
pluginSecurity.configs.recommended,
react.configs.flat.recommended,
react.configs.flat['jsx-runtime'],
eslintConfigPrettier
],
settings: {
'boundaries/elements': []
},
rules: {
...boundaries.configs.recommended.rules
},
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
ecmaFeatures: { jsx: true }
}
}
}
);
```
Create the `.eslintignore` file:
```bash
touch .eslintignore
```
Ignore the following files and directories in Eslint check:
```
.git/
.idea/
.vscode/
node_modules/
.next/
build/
dist/
coverage/
out/
```
Rename the `lint` command to `lint.es` in the `scripts` property of the `package.json` file:
```json
"lint.es": "next lint",
```
### Add [Commitlint](https://commitlint.js.org/)
```bash
pnpm i -D @commitlint/{cli,config-conventional}
node --eval "fs.writeFileSync('.commitlintrc.mjs','export default { extends: [\'@commitlint/config-conventional\'] };')"
```
Add the `lint.commit` command to the `scripts` property in `package.json` file:
```json
"lint.commit": "commitlint --from origin/main",
```
### Add [lint-staged](https://github.com/lint-staged/lint-staged)
```bash
pnpm i -D lint-staged
```
Create `.lintstagedrc` file:
```bash
touch .lintstagedrc
```
Insert the following rules:
```json
{
"*.(js|mjs|jsx|ts|mts|tsx)": ["eslint", "prettier --write"],
"*.(css)": ["stylelint", "prettier --write"],
"*.(md|json|xml|html|sh|yml|yaml)": "prettier --write"
}
```
### Add [Husky](https://typicode.github.io/husky/)
```bash
pnpm i -D husky
npx husky init
node --eval "fs.writeFileSync('.husky/pre-commit','pnpm exec lint-staged\n')"
node --eval "fs.writeFileSync('.husky/commit-msg','pnpm exec commitlint --edit \$1\n')"
```
### Add [dotenv](https://github.com/motdotla/dotenv#readme)
```bash
pnpm i dotenv
```
Create `.env.example` file:
```bash
touch .env.example
```
Add `.env` file to `.gitignore` under `# local env files`
### Make the commit for the configuration.
```bash
git add . && git commit -m"chore: application setup and configuration"
```
---
[Next](https://hackmd.io/pVySIYBOSQGH30s7hPQ_Bg)
[Home](https://hackmd.io/9GoEP2IbT6SIJQHOf03PbA)