Vite 6 + React + TypeScript : créer une application web moderne de A à Z en 2026

Noqta Team
Par Noqta Team ·

Chargement du lecteur de synthèse vocale...

Vite 6 redéfinit le développement frontend. Avec sa nouvelle Environment API, un HMR encore plus rapide et un écosystème de plugins mature, Vite 6 est devenu le standard de facto pour les projets React + TypeScript en 2026. Dans ce guide, vous construirez une application complète en exploitant chaque fonctionnalité clé de Vite 6.

Ce que vous apprendrez

À la fin de ce guide, vous saurez :

  • Créer et configurer un projet Vite 6 + React + TypeScript de zéro
  • Comprendre et utiliser la nouvelle Environment API de Vite 6
  • Configurer le Hot Module Replacement (HMR) pour un développement ultra-rapide
  • Optimiser le bundling de production avec le tree-shaking et le code splitting
  • Écrire et exécuter des tests avec Vitest
  • Configurer les variables d'environnement et les proxies API
  • Ajouter du CSS moderne avec CSS Modules et PostCSS
  • Déployer votre application en production

Prérequis

Avant de commencer, assurez-vous d'avoir :

  • Node.js 20+ installé (ou Bun 1.1+)
  • Des connaissances de base en React (composants, hooks, JSX)
  • Une familiarité avec TypeScript (types, interfaces, génériques)
  • Un éditeur de code — VS Code ou Cursor recommandé
  • Un terminal avec accès à npm ou pnpm

Pourquoi Vite 6 ?

Depuis sa première version en 2020, Vite a révolutionné le développement frontend en remplaçant les bundlers lourds comme webpack par un serveur de développement basé sur les modules ES natifs. Vite 6, sorti fin 2025, apporte des améliorations majeures :

FonctionnalitéVite 5Vite 6
Environment APIAbsenteComplète et stable
Serveur de devRapideEncore plus rapide
Support de RolldownExpérimentalStable
Résolution des modulesStandardOptimisée
Configuration TypeScriptBonneExcellente avec autocomplétion

La principale nouveauté de Vite 6 est l'Environment API — une abstraction qui permet de gérer plusieurs environnements d'exécution (client, SSR, worklets) dans un même projet avec des configurations distinctes.


Étape 1 : Créer le projet

Commençons par scaffolder un nouveau projet avec le template React + TypeScript officiel de Vite :

npm create vite@latest mon-app-vite -- --template react-ts
cd mon-app-vite
npm install

Si vous préférez pnpm (recommandé pour les performances) :

pnpm create vite mon-app-vite --template react-ts
cd mon-app-vite
pnpm install

Structure du projet

Voici la structure générée :

mon-app-vite/
├── public/
│   └── vite.svg
├── src/
│   ├── assets/
│   │   └── react.svg
│   ├── App.css
│   ├── App.tsx
│   ├── index.css
│   ├── main.tsx
│   └── vite-env.d.ts
├── index.html
├── package.json
├── tsconfig.json
├── tsconfig.app.json
├── tsconfig.node.json
└── vite.config.ts

Contrairement aux bundlers traditionnels, index.html est à la racine du projet — Vite l'utilise comme point d'entrée plutôt qu'un fichier JavaScript.

Lancez le serveur de développement :

npm run dev

Ouvrez http://localhost:5173 — vous verrez l'application React par défaut avec le HMR activé. Modifiez src/App.tsx et observez les changements instantanés dans le navigateur.


Étape 2 : Comprendre la configuration Vite 6

Ouvrez vite.config.ts — le cœur de votre configuration :

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
 
export default defineConfig({
  plugins: [react()],
})

Configuration avancée

Enrichissons cette configuration pour un projet de production :

import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
import { resolve } from 'path'
 
export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '')
 
  return {
    plugins: [react()],
 
    // Alias de chemins pour des imports propres
    resolve: {
      alias: {
        '@': resolve(__dirname, './src'),
        '@components': resolve(__dirname, './src/components'),
        '@hooks': resolve(__dirname, './src/hooks'),
        '@utils': resolve(__dirname, './src/utils'),
        '@types': resolve(__dirname, './src/types'),
      },
    },
 
    // Configuration du serveur de développement
    server: {
      port: 3000,
      open: true,
      // Proxy pour les appels API en développement
      proxy: {
        '/api': {
          target: env.VITE_API_URL || 'http://localhost:8080',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, ''),
        },
      },
    },
 
    // Configuration du build de production
    build: {
      target: 'es2022',
      sourcemap: true,
      rollupOptions: {
        output: {
          manualChunks: {
            vendor: ['react', 'react-dom'],
          },
        },
      },
    },
 
    // Configuration CSS
    css: {
      modules: {
        localsConvention: 'camelCase',
      },
    },
  }
})

Configurer les alias TypeScript

Pour que TypeScript reconnaisse les alias @, mettez à jour tsconfig.app.json :

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@components/*": ["./src/components/*"],
      "@hooks/*": ["./src/hooks/*"],
      "@utils/*": ["./src/utils/*"],
      "@types/*": ["./src/types/*"]
    }
  }
}

Étape 3 : L'Environment API de Vite 6

La fonctionnalité phare de Vite 6 est l'Environment API. Avant Vite 6, le serveur de développement traitait tout le code dans un seul pipeline. Désormais, chaque environnement (client, SSR, worker) a sa propre configuration et son propre module graph.

Pourquoi c'est important

Imaginons un projet avec du rendu côté serveur (SSR). Avant Vite 6, le même pipeline transformait le code client et le code serveur, ce qui pouvait causer des conflits. Avec l'Environment API :

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
 
export default defineConfig({
  plugins: [react()],
 
  environments: {
    // Environnement client (navigateur)
    client: {
      build: {
        outDir: 'dist/client',
        rollupOptions: {
          input: './index.html',
        },
      },
    },
 
    // Environnement SSR (Node.js)
    ssr: {
      build: {
        outDir: 'dist/server',
        ssr: true,
      },
      resolve: {
        conditions: ['node'],
      },
    },
  },
})

Utilisation dans les plugins

Si vous développez des plugins Vite, l'Environment API vous permet de cibler un environnement spécifique :

function monPluginVite() {
  return {
    name: 'mon-plugin',
 
    // Ce hook s'exécute dans chaque environnement
    resolveId(source, importer, options) {
      if (this.environment.name === 'ssr') {
        // Logique spécifique au SSR
        return null
      }
      // Logique par défaut (client)
      return null
    },
 
    // Configurer différemment selon l'environnement
    configureServer(server) {
      const clientEnv = server.environments.client
      const ssrEnv = server.environments.ssr
 
      // Chaque environnement a son propre module graph
      console.log('Modules client:', clientEnv.moduleGraph.idToModuleMap.size)
    },
  }
}

Pour la plupart des projets React classiques (SPA sans SSR), l'Environment API fonctionne automatiquement avec l'environnement client par défaut. Son intérêt principal se manifeste dans les frameworks full-stack comme Next.js, Remix ou Astro qui utilisent Vite en interne.


Étape 4 : Structurer le projet React

Créons une architecture de projet scalable :

mkdir -p src/{components,hooks,utils,types,pages,styles,services}

Layout principal

Créez src/components/Layout.tsx :

import { Outlet } from 'react-router-dom'
import { Header } from './Header'
import { Footer } from './Footer'
import styles from './Layout.module.css'
 
export function Layout() {
  return (
    <div className={styles.layout}>
      <Header />
      <main className={styles.main}>
        <Outlet />
      </main>
      <Footer />
    </div>
  )
}

CSS Modules avec Vite

Créez src/components/Layout.module.css — Vite supporte les CSS Modules nativement :

.layout {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
 
.main {
  flex: 1;
  max-width: 1200px;
  margin: 0 auto;
  padding: 2rem;
  width: 100%;
}

Composant Header

Créez src/components/Header.tsx :

import { Link, NavLink } from 'react-router-dom'
import styles from './Header.module.css'
 
const navLinks = [
  { to: '/', label: 'Accueil' },
  { to: '/about', label: 'À propos' },
  { to: '/dashboard', label: 'Tableau de bord' },
]
 
export function Header() {
  return (
    <header className={styles.header}>
      <Link to="/" className={styles.logo}>
        Mon App Vite
      </Link>
      <nav className={styles.nav}>
        {navLinks.map(({ to, label }) => (
          <NavLink
            key={to}
            to={to}
            className={({ isActive }) =>
              isActive ? styles.active : styles.link
            }
          >
            {label}
          </NavLink>
        ))}
      </nav>
    </header>
  )
}

Créez src/components/Footer.tsx :

import styles from './Footer.module.css'
 
export function Footer() {
  return (
    <footer className={styles.footer}>
      <p>
        Construit avec Vite 6 + React + TypeScript &mdash;{' '}
        {new Date().getFullYear()}
      </p>
    </footer>
  )
}

Étape 5 : Routing avec React Router

Installez React Router :

npm install react-router-dom

Créez src/pages/Home.tsx :

import styles from './Home.module.css'
 
export function Home() {
  return (
    <div className={styles.home}>
      <h1>Bienvenue sur Mon App Vite 6</h1>
      <p>
        Cette application est construite avec Vite 6, React 19 et TypeScript
        pour des performances optimales.
      </p>
      <div className={styles.features}>
        <FeatureCard
          title="HMR Ultra-rapide"
          description="Modifications reflétées instantanément dans le navigateur"
        />
        <FeatureCard
          title="TypeScript Natif"
          description="Support TypeScript complet sans configuration"
        />
        <FeatureCard
          title="Build Optimisé"
          description="Tree-shaking et code splitting automatiques"
        />
      </div>
    </div>
  )
}
 
function FeatureCard({ title, description }: { title: string; description: string }) {
  return (
    <div className={styles.card}>
      <h3>{title}</h3>
      <p>{description}</p>
    </div>
  )
}

Mettez à jour src/main.tsx pour configurer le routing :

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { Layout } from '@/components/Layout'
import { Home } from '@/pages/Home'
import './index.css'
 
const router = createBrowserRouter([
  {
    element: <Layout />,
    children: [
      { index: true, element: <Home /> },
      {
        path: 'about',
        lazy: () => import('@/pages/About').then(m => ({ Component: m.About })),
      },
      {
        path: 'dashboard',
        lazy: () => import('@/pages/Dashboard').then(m => ({ Component: m.Dashboard })),
      },
    ],
  },
])
 
createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <RouterProvider router={router} />
  </StrictMode>,
)

Remarquez l'utilisation de lazy() pour le code splitting — les pages About et Dashboard seront chargées à la demande, réduisant la taille du bundle initial.


Étape 6 : Variables d'environnement

Vite 6 gère les variables d'environnement avec des fichiers .env :

# .env — chargé dans tous les modes
VITE_APP_TITLE=Mon App Vite
VITE_API_URL=https://api.exemple.com
 
# .env.development — seulement en mode dev
VITE_API_URL=http://localhost:8080
VITE_DEBUG=true
 
# .env.production — seulement en mode production
VITE_API_URL=https://api.production.com

Important : seules les variables préfixées par VITE_ sont exposées au code client. Les variables sans ce préfixe restent côté serveur pour protéger les secrets.

Typage des variables d'environnement

Créez src/env.d.ts pour le typage TypeScript :

/// <reference types="vite/client" />
 
interface ImportMetaEnv {
  readonly VITE_APP_TITLE: string
  readonly VITE_API_URL: string
  readonly VITE_DEBUG?: string
}
 
interface ImportMeta {
  readonly env: ImportMetaEnv
}

Utilisation dans votre code :

const apiUrl = import.meta.env.VITE_API_URL
const appTitle = import.meta.env.VITE_APP_TITLE
const isDev = import.meta.env.DEV // boolean automatique

Étape 7 : Fetching de données avec un hook personnalisé

Créez un hook réutilisable pour les appels API. Créez src/hooks/useFetch.ts :

import { useState, useEffect } from 'react'
 
interface FetchState<T> {
  data: T | null
  loading: boolean
  error: string | null
}
 
export function useFetch<T>(url: string): FetchState<T> {
  const [state, setState] = useState<FetchState<T>>({
    data: null,
    loading: true,
    error: null,
  })
 
  useEffect(() => {
    const controller = new AbortController()
 
    async function fetchData() {
      try {
        setState(prev => ({ ...prev, loading: true, error: null }))
        const response = await fetch(url, { signal: controller.signal })
 
        if (!response.ok) {
          throw new Error(`Erreur HTTP : ${response.status}`)
        }
 
        const data = await response.json()
        setState({ data, loading: false, error: null })
      } catch (err) {
        if (err instanceof DOMException && err.name === 'AbortError') return
        setState({
          data: null,
          loading: false,
          error: err instanceof Error ? err.message : 'Erreur inconnue',
        })
      }
    }
 
    fetchData()
    return () => controller.abort()
  }, [url])
 
  return state
}

Page Dashboard avec données dynamiques

Créez src/pages/Dashboard.tsx :

import { useFetch } from '@/hooks/useFetch'
import styles from './Dashboard.module.css'
 
interface User {
  id: number
  name: string
  email: string
}
 
export function Dashboard() {
  const { data: users, loading, error } = useFetch<User[]>(
    'https://jsonplaceholder.typicode.com/users'
  )
 
  if (loading) return <div className={styles.loading}>Chargement...</div>
  if (error) return <div className={styles.error}>Erreur : {error}</div>
 
  return (
    <div className={styles.dashboard}>
      <h1>Tableau de bord</h1>
      <div className={styles.grid}>
        {users?.map(user => (
          <div key={user.id} className={styles.userCard}>
            <h3>{user.name}</h3>
            <p>{user.email}</p>
          </div>
        ))}
      </div>
    </div>
  )
}

Étape 8 : Optimisation du build de production

Vite 6 utilise Rollup (ou bientôt Rolldown) pour le build de production. Configurons les optimisations :

Code splitting avancé

// vite.config.ts
export default defineConfig({
  build: {
    target: 'es2022',
    minify: 'esbuild',
    sourcemap: true,
 
    rollupOptions: {
      output: {
        manualChunks(id) {
          // Séparer les dépendances React dans un chunk vendor
          if (id.includes('node_modules/react')) {
            return 'react-vendor'
          }
          // Séparer React Router
          if (id.includes('node_modules/react-router')) {
            return 'router'
          }
          // Regrouper les autres dépendances
          if (id.includes('node_modules')) {
            return 'vendor'
          }
        },
      },
    },
 
    // Seuil d'avertissement pour les chunks
    chunkSizeWarningLimit: 500,
  },
})

Analyser la taille du bundle

Installez le plugin d'analyse :

npm install -D rollup-plugin-visualizer

Ajoutez-le à votre configuration :

import { visualizer } from 'rollup-plugin-visualizer'
 
export default defineConfig({
  plugins: [
    react(),
    visualizer({
      open: true,
      gzipSize: true,
      brotliSize: true,
    }),
  ],
})

Lancez le build :

npm run build

Un rapport interactif s'ouvrira dans votre navigateur, montrant la taille de chaque module et chunk.

Préchargement des assets

Vite 6 génère automatiquement les balises <link rel="modulepreload"> pour les chunks critiques. Vous pouvez aussi configurer le preload manuellement :

export default defineConfig({
  build: {
    modulePreload: {
      polyfill: true, // Polyfill pour les anciens navigateurs
    },
  },
})

Étape 9 : Tests avec Vitest

Vitest est le framework de test naturel pour Vite — il partage la même configuration et offre une compatibilité API avec Jest.

Installation

npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom

Configuration

Ajoutez la configuration Vitest dans vite.config.ts :

/// <reference types="vitest/config" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
 
export default defineConfig({
  plugins: [react()],
 
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/test/setup.ts',
    include: ['src/**/*.{test,spec}.{ts,tsx}'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      include: ['src/**/*.{ts,tsx}'],
      exclude: ['src/**/*.test.{ts,tsx}', 'src/test/**'],
    },
  },
})

Créez src/test/setup.ts :

import '@testing-library/jest-dom'

Écrire un test

Créez src/hooks/useFetch.test.ts :

import { renderHook, waitFor } from '@testing-library/react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { useFetch } from './useFetch'
 
describe('useFetch', () => {
  beforeEach(() => {
    vi.restoreAllMocks()
  })
 
  it('devrait retourner les données après un fetch réussi', async () => {
    const mockData = [{ id: 1, name: 'Test' }]
 
    global.fetch = vi.fn().mockResolvedValue({
      ok: true,
      json: () => Promise.resolve(mockData),
    })
 
    const { result } = renderHook(() =>
      useFetch('https://api.exemple.com/data')
    )
 
    expect(result.current.loading).toBe(true)
 
    await waitFor(() => {
      expect(result.current.loading).toBe(false)
      expect(result.current.data).toEqual(mockData)
      expect(result.current.error).toBeNull()
    })
  })
 
  it("devrait gérer les erreurs HTTP", async () => {
    global.fetch = vi.fn().mockResolvedValue({
      ok: false,
      status: 404,
    })
 
    const { result } = renderHook(() =>
      useFetch('https://api.exemple.com/not-found')
    )
 
    await waitFor(() => {
      expect(result.current.loading).toBe(false)
      expect(result.current.error).toBe('Erreur HTTP : 404')
      expect(result.current.data).toBeNull()
    })
  })
})

Tester un composant

Créez src/components/Header.test.tsx :

import { render, screen } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import { describe, it, expect } from 'vitest'
import { Header } from './Header'
 
describe('Header', () => {
  it('devrait afficher le logo et les liens de navigation', () => {
    render(
      <MemoryRouter>
        <Header />
      </MemoryRouter>
    )
 
    expect(screen.getByText('Mon App Vite')).toBeInTheDocument()
    expect(screen.getByText('Accueil')).toBeInTheDocument()
    expect(screen.getByText('À propos')).toBeInTheDocument()
    expect(screen.getByText('Tableau de bord')).toBeInTheDocument()
  })
})

Lancez les tests :

npx vitest          # Mode watch
npx vitest run      # Exécution unique
npx vitest --coverage  # Avec couverture

Étape 10 : Plugins Vite essentiels

Plugin SVG en composants React

npm install -D vite-plugin-svgr
import svgr from 'vite-plugin-svgr'
 
export default defineConfig({
  plugins: [
    react(),
    svgr(),
  ],
})

Utilisez des SVG comme composants :

import { ReactComponent as Logo } from '@/assets/logo.svg'
 
function App() {
  return <Logo className="logo" />
}

Plugin de compression

npm install -D vite-plugin-compression
import compression from 'vite-plugin-compression'
 
export default defineConfig({
  plugins: [
    react(),
    compression({
      algorithm: 'brotliCompress',
      ext: '.br',
    }),
    compression({
      algorithm: 'gzip',
      ext: '.gz',
    }),
  ],
})

Plugin PWA

npm install -D vite-plugin-pwa
import { VitePWA } from 'vite-plugin-pwa'
 
export default defineConfig({
  plugins: [
    react(),
    VitePWA({
      registerType: 'autoUpdate',
      manifest: {
        name: 'Mon App Vite',
        short_name: 'MonApp',
        theme_color: '#646cff',
      },
    }),
  ],
})

Étape 11 : Déploiement en production

Build de production

npm run build

Vite génère les fichiers optimisés dans le dossier dist/ :

dist/
├── assets/
│   ├── index-[hash].js      # Code de l'application
│   ├── react-vendor-[hash].js  # Chunk React
│   ├── vendor-[hash].js     # Autres dépendances
│   └── index-[hash].css     # Styles
├── index.html
└── vite.svg

Prévisualiser le build

npm run preview

Cela lance un serveur local servant les fichiers de production — parfait pour vérifier que tout fonctionne avant le déploiement.

Déployer sur Vercel

npm install -g vercel
vercel

Vercel détecte automatiquement Vite et configure le build.

Déployer sur Cloudflare Pages

npm install -D wrangler
npx wrangler pages deploy dist

Déployer sur un VPS avec Nginx

Créez un fichier nginx.conf :

server {
    listen 80;
    server_name monapp.com;
    root /var/www/mon-app-vite/dist;
    index index.html;
 
    # Compression gzip
    gzip on;
    gzip_types text/css application/javascript application/json image/svg+xml;
 
    # Cache des assets statiques (fichiers hashés)
    location /assets/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
 
    # SPA fallback
    location / {
        try_files $uri $uri/ /index.html;
    }
}

Le hashing automatique des noms de fichiers par Vite permet un cache agressif — les fichiers changent de nom à chaque modification, donc les utilisateurs obtiennent toujours la dernière version.


Dépannage

Le HMR ne fonctionne pas

Si les modifications ne se reflètent pas automatiquement :

  1. Vérifiez que votre fichier exporte un composant React (le HMR de React nécessite des exports de composants)
  2. Vérifiez la console du navigateur pour les erreurs WebSocket
  3. Essayez de redémarrer le serveur de dev : npm run dev

Erreur "Failed to resolve import"

Cela signifie généralement un alias mal configuré :

  1. Vérifiez que l'alias est défini dans vite.config.ts ET tsconfig.app.json
  2. Redémarrez le serveur de dev après avoir modifié la configuration
  3. Vérifiez que le chemin du fichier importé existe

Build trop volumineux

  1. Utilisez rollup-plugin-visualizer pour identifier les gros modules
  2. Implémentez le code splitting avec React.lazy() ou le routing lazy
  3. Vérifiez que le tree-shaking fonctionne — utilisez des imports nommés au lieu des imports par défaut depuis les grandes bibliothèques

Prochaines étapes

Maintenant que vous maîtrisez Vite 6 avec React et TypeScript, voici quelques pistes pour aller plus loin :

  • State management — intégrez Zustand ou Jotai pour la gestion d'état
  • UI Components — ajoutez shadcn/ui pour une bibliothèque de composants accessible
  • Backend — connectez votre app à une API avec tRPC ou TanStack Query
  • Tests E2E — configurez Playwright pour les tests end-to-end
  • CI/CD — mettez en place un pipeline GitHub Actions pour automatiser les tests et le déploiement

Conclusion

Vite 6 représente une étape majeure dans l'évolution du tooling frontend JavaScript. Avec la nouvelle Environment API, des performances de développement encore améliorées et un écosystème de plugins riche, il offre tout ce dont vous avez besoin pour construire des applications React + TypeScript modernes et performantes.

Les points clés à retenir :

  • Vite 6 est rapide — le HMR instantané et le build optimisé accélèrent votre workflow
  • L'Environment API ouvre de nouvelles possibilités pour le SSR et les frameworks full-stack
  • Vitest s'intègre naturellement et offre une expérience de test de premier ordre
  • L'écosystème de plugins couvre tous les besoins : PWA, SVG, compression, analyse

Que vous démarriez un nouveau projet ou que vous migriez depuis webpack ou Create React App, Vite 6 est le choix évident pour le développement frontend en 2026.


Vous voulez lire plus de tutoriels? Découvrez notre dernier tutoriel sur React Three Fiber + Next.js : Créer des expériences web 3D interactives de A à Z.

Discutez de votre projet avec nous

Nous sommes ici pour vous aider avec vos besoins en développement Web. Planifiez un appel pour discuter de votre projet et comment nous pouvons vous aider.

Trouvons les meilleures solutions pour vos besoins.

Articles connexes

Guide complet de Bun 2.0 : le runtime JavaScript tout-en-un pour 2026

Maîtrisez Bun 2.0 de zéro — le runtime JavaScript ultra-rapide qui remplace Node.js, npm, webpack et Jest en un seul binaire. Ce guide pratique couvre le runtime, le gestionnaire de paquets, le bundler, le test runner et la construction d'une API REST de production.

30 min read·