DEV Community

Cover image for Vite: Powerful React Project Setup
Camilo Martinez
Camilo Martinez

Posted on • Updated on

 

Vite: Powerful React Project Setup

Language: [πŸ‡ͺπŸ‡Έ] EspaΓ±ol - [πŸ‡ΊπŸ‡Έ] English


Vite is by far the best alternative for creating React projects today.

npm create vite@latest <project-name> -- --template <react-swc|react-swc-ts>
# npm 7+, extra double-dash is needed

cd <project-name>
npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

With these commands, we create a very basic and clean project to serve as a starting point, but it will gonna need some extra tools to automate tasks that can make your life and the life of your development team easier.


VSCode

It's recommended to make these configurations on project settings and not in the global settings.

We will start creating a .vscode folder with a settings.json file inside.

# πŸ“„ File: /.vscode/settings.json
-----------------------------------

{
    "explorer.fileNesting.patterns": {
        "*.js": "$(capture).js.map, $(capture).*.js, $(capture)_*.js, $(capture)*.snap",
        "*.jsx": "$(capture).js, $(capture).*.jsx, $(capture)_*.js, $(capture)_*.jsx, $(capture)*.snap",
        "*.ts": "$(capture).js, $(capture).d.ts.map, $(capture).*.ts, $(capture)_*.js, $(capture)_*.ts, $(capture)*.snap",
        "*.tsx": "$(capture).ts, $(capture).*.tsx, $(capture)_*.ts, $(capture)_*.tsx, $(capture)*.snap",
    },
    "emmet.excludeLanguages": [],
    "emmet.includeLanguages": {
        "markdown": "html",
        "javascript": "javascriptreact",
        "typescript": "typescriptreact"
    },
    "emmet.showSuggestionsAsSnippets": true,
    "emmet.triggerExpansionOnTab": true,
    "files.exclude": {
        "**/*.js.map": {
            "when": "$(basename)"
        },
        "**/node_modules": true,
    },
    "html.autoClosingTags": true,
    "javascript.autoClosingTags": true,
    "javascript.suggest.completeFunctionCalls": true,
    "typescript.suggest.completeFunctionCalls": true,
    "javascript.inlayHints.functionLikeReturnTypes.enabled": true,
    "typescript.inlayHints.functionLikeReturnTypes.enabled": true,
    "javascript.inlayHints.parameterNames.enabled": "all",
    "typescript.inlayHints.parameterNames.enabled": "all",
    "javascript.suggest.autoImports": true,
    "search.exclude": {
        "**/coverage": true,
        "**/node_modules": true
    },
    "typescript.autoClosingTags": true,
    "typescript.suggest.autoImports": true,
}
Enter fullscreen mode Exit fullscreen mode

There are a lot of VSCode extensions and configurations out there. If you are hungry for more check VSCode - Essentials and VSCode - React Flavored.

Debugging

There is no need to install an extra extension for debug React from VSCode.

# πŸ“„ File: /.vscode/launch.json
-----------------------------------

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "chrome",
            "request": "launch",
            "name": "Launch Chrome against localhost",
            "url": "http://localhost:3000",
            "webRoot": "${workspaceFolder}"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

If you want to disable the browser opening each time add BROWSER=none on .env file in your project. Also change the dev script to use the same port.

# πŸ“„ File: package.json
-----------------------------------

{
  "scripts": {
-   "dev": "vite",
+   "dev": "vite --port 3000",
  }
}
Enter fullscreen mode Exit fullscreen mode

Run the npm run dev command and start the browser from the Run and Debug panel. Now you can add break points directly on the VSCode.

Extensions

If you want that VSCode show extensions recommendation when open the project

# πŸ“„ File: /.vscode/extentions.json
-----------------------------------

{
    "recommendations": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "stylelint.vscode-stylelint",
        "ZixuanChen.vitest-explorer"
    ]
}
Enter fullscreen mode Exit fullscreen mode

Linter

# πŸ“„ File: /.vscode/settings.json
-----------------------------------

{ 
+    "editor.formatOnSave": true,
+    "javascript.format.enable": false,
+    "javascript.validate.enable": true,
+    "typescript.format.enable": false,
+    "typescript.validate.enable": true,
+    "editor.codeActionsOnSave": {
+        "source.fixAll.eslint": true
+    }
}
Enter fullscreen mode Exit fullscreen mode

Install and config on the project folder:

npm install -D eslint
npm init @eslint/config
Enter fullscreen mode Exit fullscreen mode

You can choose the better for you, but my opinionated configurations are:

Use: To check syntax, find problems, and enforce code style
Type of modules: JavaScript modules (import/export)
Framework: React
Typescript: No #or Yes if the project uses it
Run: Browser #and Node if use Next.js
Style guide: Popular -> Standard #JS without semi-colon ;
Format: JSON
Enter fullscreen mode Exit fullscreen mode

Semi-colon exception

Because the standard style guide does not use semi-colons (;) take this in mind. If the next statement right after the line without a semicolon starts with one of the following special operators [, (, +, \*, /, -, ,, ., it will be recognized as an expression to the previous statement. Then you are going to need to start the line with ;.

You will be asked to install extra packages. Answer yes. When finish update configurations rules:

# πŸ“„ File: .eslintrc.json
-----------------------------------

{
    "rules": {
+     "no-console": "warn",
+     "react/prop-types": "off",
+     "react/self-closing-comp": "warn",
+     "react/react-in-jsx-scope": "off"
    },
+   "settings": {
+     "react": {
+       "version": "detect"
+     }
+   }
}
Enter fullscreen mode Exit fullscreen mode

If you are using TypeScript also need to add this configuration:

# πŸ“„ File: .eslintrc.json
-----------------------------------

{
    "parserOptions": {
+     "project": ["tsconfig.json"],
+     "createDefaultProgram": true
    },
    "rules": {
      "no-console": "warn",
      "react/prop-types": "off",
      "react/self-closing-comp": "warn",
+     "@typescript-eslint/consistent-type-definitions": ["error", "type"],
+     "@typescript-eslint/explicit-function-return-type": "off",
  },
}
Enter fullscreen mode Exit fullscreen mode

Create a .eslintignore file on the root of the folder project:

# πŸ“„ File: .eslintignore
-----------------------------------

build
coverage
dist
Enter fullscreen mode Exit fullscreen mode

There is no need to add node_modules because it was ignored by default.

If you don't want to use ES Lint extensions, add list and fix command at end of scripts:

# πŸ“„ File: package.json
-----------------------------------

{
  "scripts": {
+    "lint": "eslint .",
+    "lint:fix": "eslint . --fix --ext .js,.jsx,.ts,.tsx"
  }
}
Enter fullscreen mode Exit fullscreen mode

Avoid import React error

Since React 17, you don't need to import React to use JSX anymore. But we would still need to import React to use Hooks or other exports that React provides.

To avoid ES Lint warns about import React, add a plugin:

# πŸ“„ File: .eslintrc.json
-----------------------------------

{
    "extends": [
      "plugin:react/recommended",
      "standard-with-typescript",
+     "plugin:react/jsx-runtime",    
    ],
}
Enter fullscreen mode Exit fullscreen mode

Blank Lines

If you want to preserve a blank line's previous definitions.

# πŸ“„ File: .eslintrc.json
-----------------------------------

{
    "rules": {
+       "padding-line-between-statements": [
+           "error",
+           {
+               "blankLine": "always",
+               "prev": "*",
+               "next": "return"
+           },
+           {
+               "blankLine": "always",
+               "prev": [
+                   "const",
+                   "let",
+                   "var"
+               ],
+               "next": "*"
+           },
+           {
+               "blankLine": "any",
+               "prev": [
+                   "const",
+                   "let",
+                   "var"
+               ],
+               "next": [
+                   "const",
+                   "let",
+                   "var"
+               ]
+           }
+       ]
    },
}
Enter fullscreen mode Exit fullscreen mode

Auto sort

If you don't want to deal with sorting imports and properties set this configuration.

# πŸ“„ File: .eslintrc.json
-----------------------------------

{
    "rules": {
+       "import/order": [
+           "warn",
+           {
+               "pathGroups": [
+                   {
+                       "pattern": "~/**",
+                       "group": "external",
+                       "position": "after"
+                   }
+               ],
+               "newlines-between": "always-and-inside-groups"
+           }
+       ],
+       "react/jsx-sort-props": [
+           "warn",
+           {
+               "callbacksLast": true,
+               "shorthandFirst": true,
+               "noSortAlphabetically": false,
+               "reservedFirst": true
+           }
+       ]
    },
}
Enter fullscreen mode Exit fullscreen mode

Β Format

ES Lint can be enough, and Prettier it's optional because have a better performance formatting than ES Lint. If you want to use it go ahead. But there is a problem, they fight to try both to format code, so you have to know how to configure them to work together.

If you want to use it go ahead.

# πŸ“„ File: /.vscode/settings.json
-----------------------------------

{
-    "editor.codeActionsOnSave": {
-        "source.fixAll.eslint": true
-    }
+    "eslint.alwaysShowStatus": true,
+    "eslint.probe": [
+        "javascript",
+        "javascriptreact",
+        "typescript",
+        "typescriptreact"
+    ],
+    "[javascript][typescript]": {
+        "editor.defaultFormatter": "esbenp.prettier-vscode",
+        "editor.formatOnSave": false,
         // Runs Prettier, then ESLint
+        "editor.codeActionsOnSave": [
+            "source.formatDocument",
+            "source.fixAll.eslint"
+        ],
+    }
}
Enter fullscreen mode Exit fullscreen mode

Install Prettier:

npm install -D prettier 
Enter fullscreen mode Exit fullscreen mode

And ESLint (JS) for prettier:

npm install -D eslint-config-prettier
Enter fullscreen mode Exit fullscreen mode

Or TSLint (TS) for prettier:

npm install -D tslint-config-prettier
Enter fullscreen mode Exit fullscreen mode

Create a .prettierignore file on the root of the folder project:

# πŸ“„ File: .prettierignore
-----------------------------------

build
coverage
dist
package-lock.json
Enter fullscreen mode Exit fullscreen mode

There is no need to add node_modules because it was ignored by default.

Create a .prettierrc.json file on the root of the folder project:

# πŸ“„ File: .prettierrc.json
-----------------------------------

{
    "semi": false,
    "singleQuote": true
}
Enter fullscreen mode Exit fullscreen mode

Add extends prettier configuration at the end of extends:

# πŸ“„ File: .eslintrc.json
---

{
    "extends": [
        "plugin:react/recommended",
        "standard-with-typescript",
        "plugin:react/jsx-runtime",
+       "prettier", //at last
    ],
}
Enter fullscreen mode Exit fullscreen mode

If you don't want to use prettier extensions, add check and write command at end of scripts:

# πŸ“„ File: package.json
-----------------------------------

{
  "scripts": {
+    "format": "prettier . --check",
+    "format:fix": "prettier . --write"
  }
}
Enter fullscreen mode Exit fullscreen mode

HTML Linter

npm install -D htmlhint
Enter fullscreen mode Exit fullscreen mode

If you also want to lint HTML with eslint install this additional plugin:

npm install -D eslint-plugin-html
Enter fullscreen mode Exit fullscreen mode

And add html to the list of plugins

# πŸ“„ File: .eslintrc.json
-----------------------------------

{
    "plugins": [
        "react",
+       "html"
    ],
}
Enter fullscreen mode Exit fullscreen mode

CSS Linter

Install and config on the project folder:

npm install -D stylelint stylelint-config-standard stylelint-config-idiomatic-order
Enter fullscreen mode Exit fullscreen mode

Create a configuration file named .stylelintrc.json in the top level of your repository.

# πŸ“„ File: .stylelintrc.json
-----------------------------------

{
  "extends": [
    "stylelint-config-standard",
    "stylelint-config-idiomatic-order"
  ],
  "rules": {
    "declaration-colon-newline-after": "always-multi-line"
  },
  "ignoreFiles": [
    "build/**",
    "coverage/**",
    "dist/**",
    "**/*.js",
    "**/*.jsx", 
    "**/*.ts",
    "**/*.tsx" 
  ] 
}
Enter fullscreen mode Exit fullscreen mode

If you are going to use styled components also install

npm install -D stylelint-config-styled-components
Enter fullscreen mode Exit fullscreen mode
# πŸ“„ File: .stylelintrc.json
-----------------------------------

{
  "extends": [
    "stylelint-config-standard",
    "stylelint-config-idiomatic-order",
+   "stylelint-config-styled-components"
  ],
  "ignoreFiles": [
    "build/**",
    "coverage/**",
    "dist/**",
    "**/*.js",
-   "**/*.jsx", 
    "**/*.ts",
-   "**/*.tsx" 
  ] 
}
Enter fullscreen mode Exit fullscreen mode

To prevent both VS Code's built-in linters and Stylelint from reporting the same errors, you can disable the built-in linters.

# πŸ“„ File: /.vscode/settings.json
-----------------------------------

{ 
+    "stylelint.enable": true,
+    "css.validate": false,
+    "less.validate": false,
+    "scss.validate": false,
+    "[css][scss]": {
+        "editor.defaultFormatter": "stylelint.vscode-stylelint",
+        "editor.codeActionsOnSave": [
+            "source.fixAll.stylelint"
+        ],
+    }
}
Enter fullscreen mode Exit fullscreen mode

Stylelint has more than 170ish rules. Sometimes it will show you an error that will literally cause a problem.


Git Linter

If the project doesn't have a git repository. First, run:

git init
Enter fullscreen mode Exit fullscreen mode

It works over Husky and only runs linters against staged git files. By doing so you can ensure no errors go into the repository and enforce code style.

Install and config on the project folder:

npx mrm@3 lint-staged
Enter fullscreen mode Exit fullscreen mode

Testing

Use Vitest. It's compatible with Jest language API so you don't need to relearn the syntax.

npm install -D vitest
Enter fullscreen mode Exit fullscreen mode

With the global flag you don't need to import de dependencies on each file adding automatic support to Jest.

# πŸ“„ File: vitest.config.ts
-----------------------------------

- import { defineConfig } from 'vite'
+ import { defineConfig } from 'vitest/config'

export default defineConfig({
+  test: {
+    globals: true,
+  },
})
Enter fullscreen mode Exit fullscreen mode

If you are using typescript, also add this configuration.

# πŸ“„ File: tsconfig.json
-----------------------------------

{ 
+  "compilerOptions": {
+    "types": ["vitest/globals"],
+  }
}
Enter fullscreen mode Exit fullscreen mode

The next step, it's not required. But if you want to take advantage of IntelliSense it's recommended to start the test files with:

# πŸ“„ Files: *.test.js
-----------------------------------

import { it, expect, describe } from "vitest";
Enter fullscreen mode Exit fullscreen mode

Update run test scripts like this:

# πŸ“„ File: package.json
-----------------------------------

{
  "scripts": {
+    "test":    "vitest --run --reporter verbose",
+    "test:w":  "vitest",
+    "test:ui": "vitest --ui",
  }
}
Enter fullscreen mode Exit fullscreen mode

Snapshot

If you want the snapshots to be located at the same level as test files instead of __snapshots__ folder. Add this property.

# πŸ“„ File: vitest.config.ts
-----------------------------------

export default defineConfig({
   test: {
     globals: true,
+    resolveSnapshotPath: (testPath, snapExtension) => testPath + snapExtension,
   },
})
Enter fullscreen mode Exit fullscreen mode

Coverage

For coverage reports, we need to install @vitest/coverage-c8

npm install -D @vitest/coverage-c8
Enter fullscreen mode Exit fullscreen mode

Update run test scripts like this:

# πŸ“„ File: package.json
-----------------------------------

{
  "scripts": {
+    "test:c":  "vitest run --coverage",
+    "test:cw": "vitest watch --coverage"
  }
}
Enter fullscreen mode Exit fullscreen mode

Add the @vitest/coverage-c8 configuration.

# πŸ“„ File: vitest.config.ts
-----------------------------------

import { defineConfig } from 'vitest/config'

export default defineConfig({
   test: {
     globals: true,
+    reporters: ['verbose'],
+    coverage: {
+     all: true,
+      reporter: ['text', 'html', 'lcov'],
+      include: ['**/src/**/*.{js,jsx,ts,tsx}'],
+      exclude: [
+        '**/src/main.{js,jsx,ts,tsx}',
+        '**/*.types.{ts,tsx}',
+        '**/*.test.{js,jsx,ts,tsx}',
+        '**/src/vite-env*',
+      ],
+      statements: 0,
+      branches: 0,
+      functions: 0,
+      lines: 0,
+    },
  },
})
Enter fullscreen mode Exit fullscreen mode

RTL (React Testing Library)

Run this command to install RTL.

npm install -D @testing-library/react @testing-library/dom @testing-library/user-event @testing-library/jest-dom
Enter fullscreen mode Exit fullscreen mode

Also need to install jsdom (or happy-dom)

npm install -D jsdom
Enter fullscreen mode Exit fullscreen mode

And add it as environment

# πŸ“„ File: vitest.config.ts
-----------------------------------

import { defineConfig } from 'vitest/config'

export default defineConfig({
   test: {
     globals: true,
+    environment: 'jsdom',
   },
})
Enter fullscreen mode Exit fullscreen mode

With setupFiles properties we can extend the jest-dom matchers without importing them on each test file.

# πŸ“„ File: .vitest/setup.ts
-----------------------------------

+ /* Extend Matchers */
+ import '@testing-library/jest-dom'
Enter fullscreen mode Exit fullscreen mode
# πŸ“„ File: vitest.config.ts
-----------------------------------

import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    globals: true,
+   setupFiles: './.vitest/setup.ts',
  },
})
Enter fullscreen mode Exit fullscreen mode

If you are using eslint it's good idea to add the RTL linters with this command:

npm install --D eslint-plugin-testing-library eslint-plugin-jest-dom
Enter fullscreen mode Exit fullscreen mode

And add these configurations on the eslint configuration file.

# πŸ“„ File: .eslintrc.json
-----------------------------------

{
    "extends": [
        "plugin:react/recommended",
        "standard-with-typescript",
        "plugin:react/jsx-runtime",
        "prettier",
+       "plugin:testing-library/react",
+       "plugin:jest-dom/recommended"
    ],
    "plugins": [
        "react",
        "html",
+       "testing-library",
+       "jest-dom"
    ],
    "rules": {
+       "testing-library/await-async-query": "error",
+       "testing-library/no-await-sync-query": "error",
+       "testing-library/no-debugging-utils": "warn",
+       "testing-library/no-dom-import": "off",
+       "jest-dom/prefer-checked": "error",
+       "jest-dom/prefer-enabled-disabled": "error",
+       "jest-dom/prefer-required": "error",
+       "jest-dom/prefer-to-have-attribute": "error"
    },
}
Enter fullscreen mode Exit fullscreen mode

Add into setupFiles a configuration to avoid the global matcher and jest-dom collisions.

# πŸ“„ File: .vitest/setup.ts
-----------------------------------

/* Extend Matchers */
import '@testing-library/jest-dom'
+ import '@testing-library/jest-dom/extend-expect'
Enter fullscreen mode Exit fullscreen mode

Mock Service Worker

If you also want to use msw to test HTTP requests you need to run this command.

npm install -D msw cross-fetch
Enter fullscreen mode Exit fullscreen mode

And add this configuration to the global setup.

# πŸ“„ File: .vitest/setup.ts
-----------------------------------

+ /* Mock Service Worker */
+ import { afterAll, afterEach, beforeAll } from 'vitest'
+ import { fetch } from 'cross-fetch'
+ import { server } from './mocks/server'

+ // Add `fetch` polyfill.
+ global.fetch = fetch

+ // Establish API mocking before all tests
+ beforeAll(() => server.listen({ onUnhandledRequest: `error` }))

+ // Reset any request handlers that we may add during the tests,
+ // so they don't affect other tests
+ afterEach(() => server.resetHandlers())

+ // Clean up after the tests are finished
+ afterAll(() => server. Close())
Enter fullscreen mode Exit fullscreen mode

Debug

For visual debugging tests.


Debug

It's not an extension. It is an npm package to install on your project that helps with debugging process.

npm install -S click-to-react-component
Enter fullscreen mode Exit fullscreen mode

Even though click-to-react-component is added to dependencies, tree-shaking will remove click-to-react-component from production builds.

beam me up

Using the alt key (or option in macOS) and with a combination of keys and clicking over the component in the browser, it will transport you to the source component in your editor.

clic-to-component

Works with vite adding these configurations on your project:

# πŸ“„ File: /src/main.jsx
-----------------------------------

import React from "react"
import ReactDOM from "react-dom/client"
+import { ClickToComponent } from "click-to-react-component"

import App from "./App"
import "./index.css"

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <App />
+   <ClickToComponent />
  </React.StrictMode>
)
Enter fullscreen mode Exit fullscreen mode

⚠ Sadly, it is not compatible with WSL, but you can use another alternative: ZSH + Oh My ZSH! on Windows without WSL


Styles

PostCSS

I actually prefer the PostCSS approach instead of Sass because it is fast, is closer to the CSS standard, and no need for the file extension rename. So in the future, you can get rid of PostCSS and your project will continue working without needing a major migration.

npm install -D postcss-cli postcss
Enter fullscreen mode Exit fullscreen mode

For me these are the essentials plugins to install:

npm install -D @csstools/postcss-nesting-experimental autoprefixer postcss-purgecss
Enter fullscreen mode Exit fullscreen mode

Install Autoprefixer only if you need old browser support. There are a lot of PostCSS plugins, try to not install too many or very rare plugins that are not closer to CSS standards (or proposal).

Create a postcss.config.js file on the root of the folder project:

# πŸ“„ File: postcss.config.js
-----------------------------------

module.exports = {
    "plugins": {
        "@csstools/postcss-nesting-experimental": true,
        "autoprefixer": true,
        "postcss-purgecss": {
            content: ["src/**/*.html"],
            css: ["src/**/*.css"]
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Plugin order is important because we need that nesting runs before autoprefixer

If you decided to use Stylelint. Add this package:

npm install --D postcss-syntax
Enter fullscreen mode Exit fullscreen mode

Add it as customSyntax on .stylelintrc.json file

# πŸ“„ File: .stylelintrc.json
-----------------------------------

{
  "extends": [
    "stylelint-config-standard"
  ],
+ "customSyntax": "postcss-syntax",
}
Enter fullscreen mode Exit fullscreen mode

Sass

If instead of PostCSS you want still to use Sass. Vite has SCSS built support. Install this package:

npm install -D sass
Enter fullscreen mode Exit fullscreen mode

Take care of using sass and not node-sass package because is deprecated.

If you decided to use Stylelint. Replace these packages:

npm remove stylelint-config-standard
npm install --D stylelint-config-standard-scss
Enter fullscreen mode Exit fullscreen mode

Replace it as extends on .stylelintrc.json file

# πŸ“„ File: .stylelintrc.json
-----------------------------------

{
  "extends": [
-    "stylelint-config-standard",
+    "stylelint-config-standard-scss"
  ]
}
Enter fullscreen mode Exit fullscreen mode

Now rename manually all .css files to .scss and update src/App.js to import src/App.scss.


That’s All Folks!
Happy Coding
πŸ––

beer

Top comments (1)

Collapse
 
wander95 profile image
Wander Sanchez

Excellent article, did not know about click to component , it is a live changer hack 😍

The most important JavaScript discussions happen on DEV

React vs Signals: 10 Years Later

React vs Signals: A Look Back

☝️ Ryan Carniato and Dan Abramov take a look back at React!