FE Next.js (JS)
Step-by-step setup for a Next.js (JavaScript) project with ESLint v9 flat config, Prettier, Stylelint, Jest via next/jest, Husky v9, and Semantic Release.
1. Install dev dependencies
1npm install -D \
2 @eslint/eslintrc \
3 eslint-config-prettier eslint-plugin-prettier \
4 prettier \
5 stylelint stylelint-config-standard \
6 jest \
7 @testing-library/react @testing-library/jest-dom @testing-library/user-event \
8 husky lint-staged \
9 @commitlint/config-conventional @commitlint/cli \
10 semantic-release @semantic-release/changelog @semantic-release/git \
11 @semantic-release/github @semantic-release/commit-analyzer \
12 @semantic-release/release-notes-generatorNote: eslint and eslint-config-next are already included when scaffolding with create-next-app.
2. Create eslint.config.mjs
1import { dirname } from 'path';
2import { fileURLToPath } from 'url';
3import { FlatCompat } from '@eslint/eslintrc';
4import prettierConfig from 'eslint-config-prettier';
5import prettier from 'eslint-plugin-prettier';
6
7const __filename = fileURLToPath(import.meta.url);
8const __dirname = dirname(__filename);
9const compat = new FlatCompat({ baseDirectory: __dirname });
10
11export default [
12 ...compat.extends('next/core-web-vitals'),
13 {
14 plugins: { prettier },
15 rules: {
16 'prettier/prettier': 'error',
17 },
18 },
19 prettierConfig,
20];Note: ESLint v9 uses flat config (eslint.config.mjs). FlatCompat bridges the legacy eslint-config-next extends syntax into flat config format.
3. Create jsconfig.json
1{
2 "compilerOptions": {
3 "paths": {
4 "@/*": ["./*"]
5 }
6 }
7}4. Create .prettierrc
1{
2 "singleQuote": true,
3 "trailingComma": "all",
4 "printWidth": 100,
5 "tabWidth": 2,
6 "semi": true
7}5. Create stylelint.config.mjs
1export default {
2 extends: ['stylelint-config-standard'],
3 rules: {
4 'at-rule-no-unknown': [
5 true,
6 {
7 ignoreAtRules: ['tailwind', 'apply', 'variants', 'responsive', 'screen'],
8 },
9 ],
10 'no-descending-specificity': null,
11 },
12};Note: stylelint-config-prettier is no longer needed — Stylelint v15+ removed all formatting rules by default.
6. Configure Husky and lint-staged
Add to package.json:
1{
2 "scripts": {
3 "prepare": "husky"
4 },
5 "lint-staged": {
6 "*.{js,jsx}": ["eslint --fix", "prettier --write"],
7 "*.{css,scss}": ["stylelint --fix", "prettier --write"],
8 "*.{json,md,yml}": ["prettier --write"]
9 }
10}Initialize Husky and set up hooks:
1npx husky init
2echo "npx lint-staged" > .husky/pre-commit
3echo "npx --no -- commitlint --edit \"$1\"" > .husky/commit-msgNote: Husky v9 replaced husky install with husky init. The prepare script is now just "husky".
7. Create commitlint.config.js
1module.exports = {
2 extends: ['@commitlint/config-conventional'],
3};8. Create jest.config.js
1const nextJest = require('next/jest');
2const createJestConfig = nextJest({ dir: './' });
3
4module.exports = createJestConfig({
5 testEnvironment: 'jsdom',
6 setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
7 moduleNameMapper: {
8 '^@/(.*)$': '<rootDir>/$1',
9 },
10 testPathIgnorePatterns: ['/node_modules/', '/.next/'],
11});Note: next/jest configures SWC transforms and handles CSS modules automatically — no identity-obj-proxy or Babel config needed.
9. Create jest.setup.js
import '@testing-library/jest-dom';10. Create release.config.js
1module.exports = {
2 branches: ['main'],
3 plugins: [
4 '@semantic-release/commit-analyzer',
5 '@semantic-release/release-notes-generator',
6 [
7 '@semantic-release/changelog',
8 {
9 changelogFile: 'CHANGELOG.md',
10 changelogTitle: '# Changelog',
11 },
12 ],
13 [
14 '@semantic-release/git',
15 {
16 assets: ['CHANGELOG.md', 'package.json', 'package-lock.json'],
17 message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
18 },
19 ],
20 '@semantic-release/github',
21 ],
22};In CI/CD, make sure GITHUB_TOKEN is available in the environment.
11. Add npm scripts to package.json
1{
2 "scripts": {
3 "lint": "eslint .",
4 "format": "prettier --write \"**/*.{js,jsx,json,css,scss,md}\"",
5 "stylelint": "stylelint \"**/*.{css,scss}\" --cache",
6 "test": "jest",
7 "test:watch": "jest --watch",
8 "test:coverage": "jest --coverage",
9 "release": "semantic-release"
10 }
11}12. Optional: VSCode settings
1{
2 "editor.formatOnSave": true,
3 "editor.codeActionsOnSave": {
4 "source.fixAll.eslint": "explicit"
5 },
6 "eslint.validate": ["javascript", "javascriptreact"],
7 "prettier.enable": true
8}