Vite, by default, doesn’t enforce TypeScript’s strictness as much as you might expect, which can lead to subtle bugs slipping into your React applications.

Let’s see Vite and React in action. Imagine a simple App.tsx in your src directory:

import React, { useState } from 'react';

function App() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <h1>Counter</h1>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default App;

And your index.html in the root:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vite + React</title>
</head>
<body>
  <div id="root"></div>
  <script type="module" src="/src/main.tsx"></script>
</body>
</html>

And src/main.tsx:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

When you run npm run dev, Vite spins up a development server with blazing-fast hot module replacement (HMR) because it leverages native ES modules, avoiding the costly bundling step of traditional bundlers during development. It serves your index.html and resolves imports directly from your source files.

The core problem Vite solves is developer experience. Traditional build tools like Webpack often require significant configuration and take a long time to start up and rebuild. Vite’s approach, using esbuild for pre-bundling dependencies and then serving your source code as native ES modules, dramatically speeds up the development loop. For TypeScript, Vite delegates type checking to your IDE and the tsc command run separately, rather than blocking the dev server.

Your tsconfig.json is where you have the most control. A minimal one might look like this:

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM"],
    "module": "ESNext",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true, // This is key!
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

Notice the strict: true option. This enables a suite of stricter type-checking options. Without it, TypeScript might infer types more loosely, and you could pass undefined to functions expecting a string, or miss type mismatches in component props, all without a compile-time error. noEmit: true is common in Vite setups because Vite itself handles the transpilation for serving, and you typically run tsc --noEmit separately for type checking during development or in CI.

The real magic of Vite’s TypeScript support is how it integrates with vue-tsc (or tsc for React) for type checking without blocking the dev server. When you run npm run dev, Vite serves your code. If you have a type error that tsc would catch, it won’t stop the server from starting. Instead, you’ll see the error reported in your terminal after the server is up and running, or when you explicitly run tsc --noEmit. This separation allows for a responsive development experience while still ensuring type safety.

One common pitfall is assuming Vite’s HMR automatically re-validates types. It doesn’t. HMR updates the runtime behavior of your code. For type checking, you must rely on tsc or your IDE’s integrated TypeScript checker. Vite itself is primarily a build tool and dev server; it doesn’t perform static type analysis. This is why noEmit: true is the default for tsconfig.json in Vite projects – the actual emission of .js files is handled by Vite’s build process, and type checking is a separate concern.

The next step is to explore Vite’s plugin system, which allows you to extend its functionality for more complex build scenarios.

Want structured learning?

Take the full Vite course →