Yevhen Klymentiev
dark
light
console
darkness
y.klymentiev@gmail.com
Reusable SnippetsPractical utility code for everyday use — custom-built and ready to share

FE Vite (JavaScript)

Step-by-step setup for a React project using Vite (JavaScript) with ESLint v9 flat config, Prettier, Stylelint, Vitest, Husky v9, and Semantic Release.

1. Scaffold the project

File
Copied!
1npm create vite@latest my-app -- --template react
2cd my-app && npm install

2. Install dev dependencies

File
Copied!
1npm install -D \
2  vitest @vitest/coverage-v8 jsdom \
3  @testing-library/react @testing-library/jest-dom @testing-library/user-event \
4  @eslint/js globals \
5  eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-react-refresh \
6  eslint-config-prettier eslint-plugin-prettier \
7  prettier \
8  stylelint stylelint-config-standard \
9  husky lint-staged \
10  @commitlint/config-conventional @commitlint/cli \
11  semantic-release @semantic-release/changelog @semantic-release/git \
12  @semantic-release/github @semantic-release/commit-analyzer \
13  @semantic-release/release-notes-generator

3. Update vite.config.js

JavaScript
Copied!
1/// <reference types="vitest" />
2import { defineConfig } from 'vite';
3import react from '@vitejs/plugin-react';
4import { resolve } from 'path';
5
6export default defineConfig({
7  plugins: [react()],
8  resolve: {
9    alias: {
10      '@': resolve(__dirname, 'src'),
11    },
12  },
13  test: {
14    globals: true,
15    environment: 'jsdom',
16    setupFiles: ['./src/setupTests.js'],
17    css: true,
18  },
19});

4. Create jsconfig.json

JSON
Copied!
1{
2  "compilerOptions": {
3    "baseUrl": ".",
4    "paths": {
5      "@/*": ["src/*"]
6    }
7  },
8  "include": ["src"]
9}

Note: Vite resolves the alias at build time; jsconfig.json paths are for VS Code IntelliSense only.

5. Create eslint.config.js

JavaScript
Copied!
1import js from '@eslint/js';
2import globals from 'globals';
3import reactHooks from 'eslint-plugin-react-hooks';
4import reactRefresh from 'eslint-plugin-react-refresh';
5import prettierConfig from 'eslint-config-prettier';
6import prettier from 'eslint-plugin-prettier';
7
8export default [
9  { ignores: ['dist'] },
10  {
11    files: ['**/*.{js,jsx}'],
12    languageOptions: {
13      ecmaVersion: 'latest',
14      sourceType: 'module',
15      globals: { ...globals.browser },
16      parserOptions: {
17        ecmaFeatures: { jsx: true },
18      },
19    },
20    plugins: {
21      'react-hooks': reactHooks,
22      'react-refresh': reactRefresh,
23      prettier,
24    },
25    rules: {
26      ...reactHooks.configs.recommended.rules,
27      'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
28      'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
29      'prettier/prettier': 'error',
30    },
31  },
32  prettierConfig,
33];

Note: ESLint v9 uses flat config (eslint.config.js) instead of the legacy .eslintrc format.

6. Create .prettierrc

JSON
Copied!
1{
2  "singleQuote": true,
3  "trailingComma": "all",
4  "printWidth": 100,
5  "tabWidth": 2,
6  "semi": true
7}

7. Create stylelint.config.mjs

JavaScript
Copied!
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, eliminating conflicts with Prettier.

8. Configure Husky and lint-staged

Add to package.json:

JSON
Copied!
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:

File
Copied!
1npx husky init
2echo "npx lint-staged" > .husky/pre-commit
3echo "npx --no -- commitlint --edit \"$1\"" > .husky/commit-msg

Note: Husky v9 replaced husky install with husky init. The prepare script is now just "husky".

9. Create commitlint.config.cjs

JavaScript
Copied!
1module.exports = {
2  extends: ['@commitlint/config-conventional'],
3};

Note: Use .cjs extension — Vite projects have "type": "module", so CommonJS configs require explicit .cjs.

10. Create src/setupTests.js

JavaScript
Copied!
import '@testing-library/jest-dom';

11. Create release.config.cjs

JavaScript
Copied!
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.

12. Add npm scripts to package.json

JSON
Copied!
1{
2  "scripts": {
3    "dev": "vite",
4    "build": "vite build",
5    "preview": "vite preview",
6    "lint": "eslint .",
7    "format": "prettier --write \"**/*.{js,jsx,json,css,scss,md}\"",
8    "stylelint": "stylelint \"**/*.{css,scss}\" --cache",
9    "test": "vitest",
10    "test:ui": "vitest --ui",
11    "test:coverage": "vitest run --coverage",
12    "release": "semantic-release"
13  }
14}

13. Optional: VSCode settings

JSON
Copied!
1{
2  "editor.formatOnSave": true,
3  "editor.codeActionsOnSave": {
4    "source.fixAll.eslint": "explicit"
5  },
6  "eslint.validate": ["javascript", "javascriptreact"],
7  "prettier.enable": true
8}