Init.

Create Next App

>_ Terminal

yarn create next-app .

Install dependencies

>_ Terminal

yarn add -D eslint prettier husky lint-staged eslint-config-prettier @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-prettier prettier-plugin-tailwindcss eslint-config-next eslint-plugin-react-hooks eslint-plugin-simple-import-sort eslint-plugin-unused-imports @next/eslint-plugin-next @commitlint/config-conventional @commitlint/cli

Prettier Config

Create two files : .prettierrc.json & .prettierignore

>_ Terminal

echo "" > .prettierrc.json

.prettierrc.json

{
	"semi": false,
	"trailingComma": "es5",
	"singleQuote": true,
	"tabWidth": 2,
	"useTabs": true,
	"bracketSpacing": true,
	"bracketSameLine": false,
	"arrowParens": "always",
	"endOfLine": "auto",
	"plugins": ["prettier-plugin-tailwindcss"]
}

>_ Terminal

echo "" > .prettierignore

.prettierignore

.next
.cache
package-lock.json
public
node_modules
next-env.d.ts
next.config.ts
yarn.lock
yarn-lock.yaml

ESLint Config

Replace content in eslint.config.mjs file

eslint.config.mjs

import { FlatCompat } from '@eslint/eslintrc'
import { dirname } from 'path'
import { fileURLToPath } from 'url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

const compat = new FlatCompat({
	baseDirectory: __dirname,
})

const eslint_config = [
	// Ignore patterns
	{
		ignores: [
			'.cache',
			'.next',
			'.yarn',
			'node_modules',
			'package-lock.json',
			'public',
			'next-env.d.ts',
			'next.config.ts',
			'yarn.lock',
			'yarn-lock.yaml',
			'src/components/ui',
			'src/services',
		],
	},

	// Extend existing configurations
	...compat.extends(
		'next',
		'next/core-web-vitals',
		'plugin:@typescript-eslint/recommended',
		'plugin:react/recommended',
		'plugin:react-hooks/recommended',
		'plugin:prettier/recommended',
		'prettier'
	),

	// Include additional plugins
	...compat.plugins(
		'@typescript-eslint',
		'prettier',
		'react',
		'react-hooks',
		'simple-import-sort',
		'unused-imports'
	),

	// Custom rules
	{
		rules: {
			// Imports & unused code
			'simple-import-sort/imports': 'error',
			'simple-import-sort/exports': 'error',
			'unused-imports/no-unused-imports': 'error',

			// TypeScript
			'@typescript-eslint/array-type': ['warn', { default: 'array' }],
			'@typescript-eslint/ban-ts-comment': 'off',
			'@typescript-eslint/consistent-type-assertions': [
				'warn',
				{ assertionStyle: 'as', objectLiteralTypeAssertions: 'never' },
			],
			'@typescript-eslint/no-empty-object-type': 'warn',
			'@typescript-eslint/no-explicit-any': 'off',
			'@typescript-eslint/no-unused-vars': 'warn',

			// React & Hooks
			'react-hooks/exhaustive-deps': 'off',
			'react-hooks/rules-of-hooks': 'error',
			'react/jsx-filename-extension': ['warn', { extensions: ['.ts', '.tsx'] }],
			'react/jsx-fragments': ['warn', 'syntax'],
			'react/no-unescaped-entities': 'off',
			'react/prop-types': 'off',
			'react/react-in-jsx-scope': 'off',
			'react/jsx-no-duplicate-props': 'warn',

			// Prettier
			'prettier/prettier': 'off',

			// General JavaScript
			'no-empty-pattern': 'warn',
			'no-unused-vars': 'warn',
			'no-var': 'warn',
			'object-shorthand': 'warn',
			'prefer-const': 'warn',
			'quote-props': ['warn', 'as-needed'],
			'no-magic-numbers': [
				'warn',
				{ ignore: [0, 1, -1], ignoreArrayIndexes: true },
			],
			eqeqeq: ['warn', 'always'],
		},
	},
]

export default eslint_config

Husky Config

>_ Terminal

npx husky init

>_ Terminal

echo "npx lint-staged" > .\.husky\pre-commit

Lint Staged Config

create a lint-staged.config.js file

>_ Terminal

echo "" > lint-staged.config.js

lint-staged.config.js

module.exports = {
	// Type check TypeScript files
	// Lint then format TypeScript files
	'*.(ts|tsx)': (filenames) => [
		'yarn tsc --noEmit --incremental --pretty',
		`yarn eslint --fix --cache ${filenames.join(' ')}`,
		`yarn prettier --write ${filenames.join(' ')}`,
	],

	// Format MarkDown and JSON
	'*.(md|json)': (filenames) => `yarn prettier --write ${filenames.join(' ')}`,
}

Commit Lint Config

>_ Terminal

yarn add -D @commitlint/config-conventional @commitlint/cli

>_ Terminal

echo "npx --no-install commitlint --edit $1" > .\.husky\commit-msg

>_ Terminal

echo "module.exports = { extends: ['@commitlint/config-conventional'] }" > commitlint.config.js

Shadcn

>_ Terminal

npx shadcn@latest init

Root layout.tsx

import "@/styles/globals.css"
import { Inter as FontSans } from "next/font/google"
 
import { cn } from "@/lib/utils"
 
const fontSans = FontSans({
  subsets: ["latin"],
  variable: "--font-sans",
})
 
export default function RootLayout({ children }: RootLayoutProps) {
  return (
    <html lang="fr" suppressHydrationWarning>
      <head />
      <body
        className={cn(
          "min-h-screen bg-background font-sans antialiased",
          fontSans.variable
        )}
      >
        ...
      </body>
    </html>
  )
}

globals.css

body {
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-sans);
}