Setup a React Vite project with TypeScript, Prettier & Vitest [2024]
Easy instructions for quick setup
Even with React frameworks like Next.js gaining popularity and React documentation advocating their use, still many developers tend to start with just using the React library without the additional tools available in the framework out of the box, as this extra layer introduces additional complexity that is unnecessary in many cases.
Since create-react-app is no longer supported, I’ve found that the best alternative for creating a new React app is to use Vite.
Below I pasted short description from the official documentation.
Vite (French word for “quick”, pronounced /vit/
, like “veet”) is a build tool that aims to provide a faster and leaner development experience for modern web projects. It consists of two major parts:
- A dev server that provides rich feature enhancements over native ES modules, for example extremely fast Hot Module Replacement (HMR).
- A build command that bundles your code with Rollup, pre-configured to output highly optimized static assets for production.
Vite is opinionated and comes with sensible defaults out of the box.
In this setup we’re going to use TypeScript. TypeScript is a typed JavaScript superset that compiles into regular JavaScript.
With the growing popularity of TypeScript or TS for short, we can consider it as an improved version of JavaScript with additional features.
It is an open-source programming language for developing large and complex applications. It was developed by Microsoft in 2012, and in fact, the reason behind its creation was the need to handle large-scale applications. It helps software developers add type safety to their projects and provides features like type aliases, interfaces, abstract classes, encapsulation, inheritance, and function overloading.
Furthermore, Angular also uses TypeScript and is actually written in it. Lastly, TypeScript is very trendy among developers today.
So, let’s create a new React project with Vite by executing the following command:
npm create vite@latest
First, you’ll be asked for project name:
npm create vite@latest
? Project name: › vite-project
Then it asks for a framework to choose:
npm create vite@latest
✔ Project name: … vite-project
? Select a framework: › - Use arrow-keys. Return to submit.
❯ Vanilla
Vue
React
Preact
Lit
Svelte
Solid
Qwik
Others
Select React and hit Enter.
Then you need to choose whether to use JavaScript/TypeSprit with Babel/SWC:
npm create vite@latest
✔ Project name: … vite-project
✔ Select a framework: › React
? Select a variant: › - Use arrow-keys. Return to submit.
❯ TypeScript
TypeScript + SWC
JavaScript
JavaScript + SWC
Let’s choose TypeScript considering to work with a big project.
It’s a final step, and then you should see something like this:
npm create vite@latest
✔ Project name: … vite-project
✔ Select a framework: › React
✔ Select a variant: › TypeScript
Scaffolding project in /Users/Oleksandr_Nedopaka/Repos/vite-project...
Done. Now run:
cd vite-project
npm install
npm run dev
Now go to the project folder, install dependencies, start dev server to check that everything works fine.
Let’s have a look what we’ve got in our package.json
now:
{
"name": "vite-project",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.56.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"typescript": "^5.2.2",
"vite": "^5.1.0"
}
}
Vite kindly preinstalls ESLint for us. In .eslintrc.cjs
we already have necessary rules for the code quality, and it provides us error highlights in our code.
Keep in mind, that you should have installed ESLint plugin to make it work.
Now let’s install Prettier to make our code look nice.
npm i -D -E prettier
And then create .prettierrc
with the following content:
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5"
}
To prevent conflicts in rules between ESLint and Prettier let’s add the following plugin:
npm i -D eslint-config-prettier
Then make changes in .eslintrc.cjs
to match the following:
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
// This disables the formatting rules in ESLint that Prettier is going to be responsible for handling.
// Make sure it's always the last config, so it gets the chance to override other configs.
'prettier',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
And then add the following script to our package.json
:
"format": "prettier --write ./src",
Now we can do npm run format
to format All our source code, but it would be great if we could format our files automatically on paste and save actions.
For this, install Prettier plugin for VS Code.
And then create .vscode/settings.json
so we can use this project as a boilerplate for every our new React project.
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnPaste": true, // required
"editor.formatOnType": false, // required
"editor.formatOnSave": true, // optional
"editor.formatOnSaveMode": "file", // required to format on save
"files.autoSave": "onFocusChange" // optional but recommended
}
Next, considering that we’re going to write unit tests for our code, let’s install the following dependencies:
npm i -D vitest jsdom @testing-library/jest-dom @testing-library/react
Then create src/__tests__/setup.ts
and insert the following code:
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
Also, then modify vite.config.ts
to match:
/// <reference types="vitest" />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['src/__tests__/setup.ts'],
},
});
Note the “triple slash command” in the first line. It is needed because the Vite config interface knows nothing about Vitest, and TS does not allow redundant properties (properties not defined by the type/interface). Therefore, Vitest must extend Vite config (defined as a TS interface).
Alternatively, instead of “tripple slash command”, you can replace standard Vite config with Vite config extended by Vitest:
import { defineConfig } from 'vitest/config';
Now we’re ready to write our first test App.test.tsx
:
import { describe, it, expect, test } from 'vitest';
import { render } from '@testing-library/react';
import App from './App';
test('demo', () => {
expect(true).toBe(true);
});
describe('render', () => {
it('renders the main page', () => {
render(<App />);
expect(true).toBeTruthy();
});
});
Finally, add the following script in package.json
:
"test": "vitest"
Your package.json
should look similar to:
{
"name": "vite-project",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"format": "prettier --write ./src",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"test": "vitest"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.2.1",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"jsdom": "^24.0.0",
"prettier": "3.2.5",
"typescript": "^5.2.2",
"vite": "^5.1.0",
"vitest": "^1.2.2"
}
}
Run npm test
to check if everything works as expected.
Finnaly, here’s what you should pay attention to. Our current .eslintrc.cjs
contains only very basic recommended rules, and that’s fine if you need to develop a simple application without micro front-ends and extensive use of various APIs or if you are just starting to use TS. However, in most cases the current basic settings are not enough, and if you’re going to develop a complex production application, it’s better to update your configuration to include type-aware lint rules. Let’s do it!
Install React-specific linting rules for ESLint:
npm i -D eslint-plugin-react
Then make changes in .eslintrc.cjs
to match the following:
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:@typescript-eslint/recommended-type-checked',
'plugin:@typescript-eslint/stylistic-type-checked',
'plugin:react-hooks/recommended',
// This disables the formatting rules in ESLint that Prettier is going to be responsible for handling.
// Make sure it's always the last config, so it gets the chance to override other configs.
'prettier',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
};
If you need more hard sort of rules you can replace plugin:@typescript-eslint/recommended-type-checked
with plugin:@typescript-eslint/strict-type-checked
to ensure strictly typed code.
Now you can use this project as a boilerplate for your next React application.
That’s it, folks! Happy coding!