Create Next App
>_ Terminal
yarn create next-app .
>_ Terminal
yarn create next-app .
>_ 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
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
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
>_ Terminal
npx husky init
>_ Terminal
echo "npx lint-staged" > .\.husky\pre-commit
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(' ')}`,
}
>_ 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
>_ 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);
}