package.json dependencies in a Vite + React + TypeScript project
Table of Contents
- The two categories: dependencies vs devDependencies
- dependencies — shipped to the browser
- devDependencies — build-time tools only
- scripts — what the commands do
- overrides — forcing a specific version
- The big picture
The two categories: dependencies vs devDependencies
{
"dependencies": { ... },
"devDependencies": { ... }
}
| Category | When used | Included in production build? |
|---|---|---|
dependencies | Runtime — code your app needs in the browser | Yes |
devDependencies | Build time — tools that help you develop and build | No |
In a Vite project the distinction matters less for the final bundle (Vite tree-shakes everything anyway), but it is important conceptually and for server-side deployments.
dependencies — shipped to the browser
"dependencies": {
"@tailwindcss/vite": "^4.2.1",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"tailwindcss": "^4.2.1"
}
react
The core React library. It provides:
useState,useEffect,useRef, and all other hooks- The virtual DOM diffing engine
- The component model
Without this package, there is no React.
react-dom
The renderer that connects React to the real browser DOM.
React itself is platform-agnostic — react-dom is what knows how to
turn your virtual DOM into actual <div> elements on the page.
// main.tsx — this is where react-dom is used
import ReactDOM from 'react-dom/client'
import App from './App'
ReactDOM.createRoot(document.getElementById('root')!).render(<App />)
If you were building a React Native app, you'd use react-native instead of react-dom.
tailwindcss
The Tailwind CSS framework itself. It provides all the utility classes
(flex, p-4, text-blue-600, etc.) that you use directly in your JSX.
@tailwindcss/vite
A Vite plugin that integrates Tailwind CSS into the Vite build pipeline. It handles scanning your source files for used class names and generating the final CSS at build time.
You register it in vite.config.ts:
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
})
devDependencies — build-time tools only
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/node": "^24.10.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"babel-plugin-react-compiler": "^1.0.0",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.48.0",
"vite": "^8.0.0-beta.13"
}
vite
The build tool and development server. It does two things:
- Dev mode (
npm run dev): serves your files instantly using native ES modules, with Hot Module Replacement (HMR) so the browser updates without a full page reload. - Build mode (
npm run build): bundles everything into optimized static files indist/.
Vite uses esbuild under the hood, which is written in Go and is
10–100× faster than webpack-based alternatives.
typescript
The TypeScript compiler (tsc). It reads your .ts and .tsx files
and does two things:
- Type checks your code — catches type errors before they reach the browser
- Can compile TypeScript to JavaScript (but in Vite projects,
noEmit: truemeans it only type-checks; Vite/esbuild handles the actual compilation)
@vitejs/plugin-react
A Vite plugin that adds React support:
- Enables JSX/TSX transformation (turns
<MyComponent />intoReact.createElement(...)) - Enables Fast Refresh — when you edit a component, only that component re-renders in the browser without losing state
// vite.config.ts
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
})
babel-plugin-react-compiler
The React Compiler is an experimental
compiler from the React team that automatically memoizes your components.
It runs as a Babel plugin during the build, transforming your components so you
don't need to write useMemo and useCallback manually.
You pass it to the React Vite plugin:
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
react({
babel: {
plugins: [['babel-plugin-react-compiler', {}]],
},
}),
],
})
@types/react and @types/react-dom
TypeScript type definitions for React and ReactDOM.
The react and react-dom packages themselves are plain JavaScript — they don't
include TypeScript types. These @types/ packages are separate, community-maintained
(via DefinitelyTyped) files that
tell TypeScript what every React API looks like:
// TypeScript knows this is valid because of @types/react:
const [count, setCount] = useState<number>(0)
// ↑ number ↑ Dispatch<SetStateAction<number>>
Without @types/react, TypeScript would show errors on every React import.
@types/node
TypeScript type definitions for Node.js built-in APIs.
Even in a browser project, the Vite config (vite.config.ts) runs in Node.js,
so it needs types for things like process, __dirname, and the path module:
// vite.config.ts — needs @types/node for this to type-check
import path from 'path'
export default defineConfig({
resolve: {
alias: { '@': path.resolve(__dirname, './src') },
},
})
eslint
The JavaScript/TypeScript linter. It statically analyzes your code for common problems — not type errors (that's TypeScript's job), but things like:
- Unused variables
- Missing
keyprops in React lists - Calling hooks conditionally
typescript-eslint
Connects ESLint with TypeScript. It provides:
- A parser that lets ESLint understand TypeScript syntax
- Rules that are TypeScript-aware (e.g.,
no-explicit-any,no-floating-promises)
Without this, ESLint would not understand .tsx files.
@eslint/js
ESLint's own recommended rule set for JavaScript.
It's the base layer of rules that typescript-eslint and other plugins build on top of.
eslint-plugin-react-hooks
ESLint rules that enforce the Rules of Hooks:
- Hooks must be called at the top level (not inside
if, loops, etc.) - Hooks must only be called from React components or custom hooks
These are the rules that catch common hook bugs before they cause subtle runtime issues.
eslint-plugin-react-refresh
ESLint rules that ensure your components are compatible with Vite's Fast Refresh. For example, it warns when a file exports both a component and a non-component value, which would cause Fast Refresh to do a full page reload instead of a hot update.
globals
A data package that provides lists of known global variables for different environments
(browser, Node.js, etc.). ESLint uses it to know which global names
(window, document, process, console, ...) are valid in each context,
so it doesn't flag them as "undefined variable" errors.
scripts — what the commands do
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
}
npm run dev
Starts the Vite development server with HMR at http://localhost:5173.
npm run build
Runs two steps in sequence:
tsc -b && vite build
tsc -b— type-checks the entire project using project references (tsconfig.json). If there are any TypeScript errors, the build stops here.vite build— compiles and bundles everything intodist/for production.
This ensures you never ship a build with type errors.
npm run lint
Runs ESLint on all files in the project (eslint .).
npm run preview
Serves the production dist/ folder locally so you can test your production build
before deploying. It does not start a dev server with HMR.
overrides — forcing a specific version
"overrides": {
"vite": "^8.0.0-beta.13"
}
overrides forces npm to use a specific version of a package, even when
other dependencies request a different version.
Here it pins Vite to a beta version. Since this project uses Vite 8 beta, which other packages may not have declared as a peer dependency yet, the override ensures everything uses the same Vite version and prevents npm from installing an older stable version as a sub-dependency.
The big picture
Here is how all the pieces connect at different stages:
┌─ Editor / save ──────────────────────────────────────────────┐
│ typescript + @types/react + @types/react-dom + @types/node │
│ → type errors shown in VS Code in real time │
└──────────────────────────────────────────────────────────────┘
┌─ npm run dev ────────────────────────────────────────────────┐
│ vite │
│ └─ @vitejs/plugin-react → JSX transform + Fast Refresh │
│ └─ @tailwindcss/vite → CSS generation │
│ └─ babel-plugin-react-compiler → auto-memoization │
└──────────────────────────────────────────────────────────────┘
┌─ npm run build ──────────────────────────────────────────────┐
│ tsc -b → type check (stops build if errors found) │
│ vite build → bundle into dist/ │
└──────────────────────────────────────────────────────────────┘
┌─ npm run lint ───────────────────────────────────────────────┐
│ eslint │
│ └─ @eslint/js → JS rules │
│ └─ typescript-eslint → TS-aware rules │
│ └─ eslint-plugin-react-hooks → hook rules │
│ └─ eslint-plugin-react-refresh → HMR compatibility rules │
│ └─ globals → known browser/node globals │
└──────────────────────────────────────────────────────────────┘
┌─ Browser (runtime) ──────────────────────────────────────────┐
│ react + react-dom + tailwindcss │
│ (everything else was stripped out — devDependencies are │
│ never included in the final bundle) │
└──────────────────────────────────────────────────────────────┘