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

Un seul binaire pour les gouverner tous. Bun 2.0 n'est pas simplement un autre runtime JavaScript — c'est une boîte à outils complète qui remplace Node.js, npm, webpack et Jest par un seul binaire ultra-rapide écrit en Zig. Dans ce guide pratique, vous explorerez chaque fonctionnalité majeure de Bun et construirez une API REST prête pour la production à partir de zéro.
Ce que vous apprendrez
À la fin de ce guide, vous saurez :
- Installer et configurer Bun 2.0 comme runtime JavaScript principal
- Utiliser le gestionnaire de paquets Bun pour installer les dépendances plus vite que npm, yarn ou pnpm
- Exploiter le support natif de TypeScript et JSX sans aucune configuration
- Construire et optimiser des projets avec le bundler Bun
- Écrire et exécuter des tests avec le test runner Bun intégré
- Utiliser les API Bun — I/O fichier, serveur HTTP, SQLite, WebSocket et plus
- Construire une API REST complète avec le serveur HTTP natif de Bun
- Déployer une application Bun en production
Prérequis
Avant de commencer, assurez-vous d'avoir :
- Des connaissances de base en JavaScript/TypeScript (fonctions, async/await, modules)
- Une familiarité avec le terminal (exécution de commandes, navigation)
- macOS, Linux ou WSL (Bun supporte les trois)
- Un éditeur de code — VS Code ou Cursor recommandé
Pourquoi Bun ?
L'écosystème JavaScript a traditionnellement nécessité plusieurs outils pour différentes tâches : Node.js pour exécuter le code, npm pour les paquets, webpack ou esbuild pour le bundling, et Jest ou Vitest pour les tests. Bun regroupe tout cela dans un seul binaire :
| Fonctionnalité | Stack traditionnel | Bun |
|---|---|---|
| Runtime | Node.js | Bun |
| Gestionnaire de paquets | npm / yarn / pnpm | bun install |
| Bundler | webpack / esbuild / Rollup | bun build |
| Test runner | Jest / Vitest | bun test |
| TypeScript | tsc + ts-node | Support natif |
| Chargement .env | paquet dotenv | Intégré |
Bun atteint cette vitesse grâce à son noyau écrit en Zig, le moteur JavaScriptCore (de Safari) et des optimisations système agressives. Les installations de paquets qui prennent 30 secondes avec npm se terminent en moins de 2 secondes avec Bun.
Étape 1 : Installer Bun
Installez Bun avec une seule commande :
curl -fsSL https://bun.sh/install | bashSur macOS, vous pouvez aussi utiliser Homebrew :
brew install oven-sh/bun/bunVérifiez l'installation :
bun --version
# 2.x.xBun s'installe dans ~/.bun/bin/bun et s'ajoute automatiquement à votre PATH.
Mettre à jour Bun
Gardez Bun à jour avec :
bun upgradeÉtape 2 : Votre premier projet Bun
Créez un nouveau projet avec bun init :
mkdir bun-demo && cd bun-demo
bun initCela génère une structure de projet minimale :
bun-demo/
├── index.ts
├── package.json
├── tsconfig.json
└── README.md
Notez que Bun utilise TypeScript par défaut — aucune configuration supplémentaire nécessaire. Exécutez le fichier :
bun run index.ts
# Hello via Bun!Pas d'étape de compilation. Bun transpile TypeScript à la volée avec un overhead quasi nul.
Étape 3 : Bun comme gestionnaire de paquets
Le gestionnaire de paquets Bun est un remplacement direct de npm, yarn et pnpm. Il lit le même package.json et génère un dossier node_modules compatible.
Installer des dépendances
# Installer toutes les dépendances depuis package.json
bun install
# Ajouter une dépendance
bun add zod
# Ajouter une dépendance de développement
bun add -d @types/node
# Supprimer une dépendance
bun remove zodComparaison de vitesse
Bun utilise un cache global de modules et des liens physiques au lieu de copier les fichiers. Un bun install typique sur un projet Next.js se termine en moins de 2 secondes, contre 15 à 30 secondes avec npm.
Fichier de verrouillage
Bun génère un fichier de verrouillage binaire appelé bun.lockb. Il est déterministe et beaucoup plus rapide à analyser que package-lock.json. Si vous avez besoin d'un fichier texte pour la revue de code :
bun install --yarn
# Génère yarn.lock à côté de bun.lockbWorkspaces
Bun supporte nativement les workspaces npm :
{
"name": "monorepo",
"workspaces": ["packages/*", "apps/*"]
}# Installer toutes les dépendances des workspaces
bun install
# Exécuter un script dans un workspace spécifique
bun run --filter @myorg/api startÉtape 4 : Support natif de TypeScript et JSX
Bun exécute directement les fichiers .ts, .tsx, .js et .jsx sans aucune étape de build ou configuration. Le transpileur intégré gère :
- TypeScript avec support complet de la syntaxe de types
- La transformation JSX/TSX
- Les décorateurs (TC39 et legacy)
- Le top-level await
- L'interopérabilité ES modules et CommonJS
Créez un fichier types-demo.ts :
interface User {
id: number;
name: string;
email: string;
}
function greet(user: User): string {
return `Hello, ${user.name}! Your email is ${user.email}.`;
}
const user: User = {
id: 1,
name: "Ahmed",
email: "ahmed@example.com",
};
console.log(greet(user));Exécutez-le directement :
bun run types-demo.ts
# Hello, Ahmed! Your email is ahmed@example.com.Pas de tsc, pas de ts-node, pas de tsx — juste Bun.
Étape 5 : Les API Bun — La bibliothèque standard intégrée
Bun fournit un ensemble riche d'API intégrées qui remplacent les paquets npm courants.
5.1 : I/O fichier avec Bun.file()
// Écrire un fichier
await Bun.write("output.txt", "Hello from Bun!");
// Lire un fichier
const file = Bun.file("output.txt");
const text = await file.text();
console.log(text); // "Hello from Bun!"
// Obtenir les métadonnées du fichier
console.log(file.size); // en octets
console.log(file.type); // type MIMEBun.file() est lazy — il ne lit pas le fichier tant que vous n'appelez pas .text(), .json(), .arrayBuffer() ou .stream().
5.2 : Variables d'environnement
Bun charge automatiquement les fichiers .env sans aucun paquet :
# .env
DATABASE_URL=postgres://localhost:5432/mydb
API_KEY=secret123// Accès direct — pas besoin d'importer dotenv
console.log(Bun.env.DATABASE_URL);
console.log(Bun.env.API_KEY);5.3 : Hachage de mots de passe
// Hacher un mot de passe (bcrypt sous le capot)
const hash = await Bun.password.hash("my-secure-password");
// Vérifier un mot de passe
const isValid = await Bun.password.verify("my-secure-password", hash);
console.log(isValid); // true5.4 : SQLite intégré
Bun est livré avec un driver SQLite natif — aucun paquet npm requis :
import { Database } from "bun:sqlite";
const db = new Database("myapp.db");
// Créer une table
db.run(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
)
`);
// Insérer des données
const insert = db.prepare("INSERT INTO users (name, email) VALUES (?, ?)");
insert.run("Ahmed", "ahmed@example.com");
insert.run("Sara", "sara@example.com");
// Requêter les données
const users = db.query("SELECT * FROM users").all();
console.log(users);5.5 : Hachage et cryptographie
// Hachage rapide
const hash = Bun.hash("hello world");
console.log(hash);
// Hachage cryptographique
const hasher = new Bun.CryptoHasher("sha256");
hasher.update("hello world");
console.log(hasher.digest("hex"));Étape 6 : Construire un serveur HTTP
Le serveur HTTP natif de Bun est l'une de ses fonctionnalités phares — il gère des centaines de milliers de requêtes par seconde.
Serveur basique
// server.ts
Bun.serve({
port: 3000,
fetch(req) {
return new Response("Hello from Bun!");
},
});
console.log("Server running at http://localhost:3000");bun run server.tsLe handler fetch utilise les objets standard Request et Response de la Web API, rendant votre code portable entre Bun, Deno et Cloudflare Workers.
Routage
Construisez un routeur simple avec le pattern matching d'URL :
// router.ts
Bun.serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/" && req.method === "GET") {
return Response.json({ message: "Welcome to the API" });
}
if (url.pathname === "/health" && req.method === "GET") {
return Response.json({ status: "ok", uptime: process.uptime() });
}
return new Response("Not Found", { status: 404 });
},
});Gestion des corps JSON
Bun.serve({
port: 3000,
async fetch(req) {
if (req.method === "POST" && new URL(req.url).pathname === "/users") {
const body = await req.json();
// Traiter le body...
return Response.json(
{ message: "User created", user: body },
{ status: 201 }
);
}
return new Response("Not Found", { status: 404 });
},
});Étape 7 : Construire une API REST complète
Combinons tout dans une API REST de qualité production — un gestionnaire de tâches avec persistance SQLite.
Structure du projet
bun-tasks-api/
├── src/
│ ├── index.ts # Point d'entrée
│ ├── router.ts # Gestionnaire de routes
│ ├── db.ts # Configuration base de données
│ ├── handlers/
│ │ └── tasks.ts # Handlers CRUD des tâches
│ └── types.ts # Définitions de types
├── tests/
│ └── tasks.test.ts # Tests API
├── package.json
└── tsconfig.json
Initialiser le projet
mkdir bun-tasks-api && cd bun-tasks-api
bun init
mkdir -p src/handlers tests7.1 : Définitions de types
// src/types.ts
export interface Task {
id: number;
title: string;
description: string | null;
completed: boolean;
created_at: string;
updated_at: string;
}
export interface CreateTaskInput {
title: string;
description?: string;
}
export interface UpdateTaskInput {
title?: string;
description?: string;
completed?: boolean;
}7.2 : Couche base de données
// src/db.ts
import { Database } from "bun:sqlite";
const db = new Database("tasks.db");
// Activer le mode WAL pour de meilleures performances concurrentes
db.run("PRAGMA journal_mode = WAL");
// Créer les tables
db.run(`
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT,
completed INTEGER DEFAULT 0,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
)
`);
export default db;7.3 : Handlers des tâches
// src/handlers/tasks.ts
import db from "../db";
import type { CreateTaskInput, UpdateTaskInput, Task } from "../types";
export function getAllTasks(): Task[] {
return db.query("SELECT * FROM tasks ORDER BY created_at DESC").all() as Task[];
}
export function getTaskById(id: number): Task | null {
return db.query("SELECT * FROM tasks WHERE id = ?").get(id) as Task | null;
}
export function createTask(input: CreateTaskInput): Task {
const stmt = db.prepare(
"INSERT INTO tasks (title, description) VALUES (?, ?) RETURNING *"
);
return stmt.get(input.title, input.description ?? null) as Task;
}
export function updateTask(id: number, input: UpdateTaskInput): Task | null {
const existing = getTaskById(id);
if (!existing) return null;
const title = input.title ?? existing.title;
const description = input.description ?? existing.description;
const completed = input.completed !== undefined
? (input.completed ? 1 : 0)
: existing.completed;
const stmt = db.prepare(`
UPDATE tasks
SET title = ?, description = ?, completed = ?, updated_at = datetime('now')
WHERE id = ?
RETURNING *
`);
return stmt.get(title, description, completed, id) as Task;
}
export function deleteTask(id: number): boolean {
const result = db.run("DELETE FROM tasks WHERE id = ?", [id]);
return result.changes > 0;
}7.4 : Le routeur
// src/router.ts
import {
getAllTasks,
getTaskById,
createTask,
updateTask,
deleteTask,
} from "./handlers/tasks";
export async function handleRequest(req: Request): Promise<Response> {
const url = new URL(req.url);
const path = url.pathname;
const method = req.method;
// En-têtes CORS
const headers = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
};
if (method === "OPTIONS") {
return new Response(null, { status: 204, headers });
}
// GET /api/tasks
if (path === "/api/tasks" && method === "GET") {
const tasks = getAllTasks();
return Response.json({ data: tasks }, { headers });
}
// GET /api/tasks/:id
const taskMatch = path.match(/^\/api\/tasks\/(\d+)$/);
if (taskMatch && method === "GET") {
const task = getTaskById(Number(taskMatch[1]));
if (!task) {
return Response.json(
{ error: "Task not found" },
{ status: 404, headers }
);
}
return Response.json({ data: task }, { headers });
}
// POST /api/tasks
if (path === "/api/tasks" && method === "POST") {
const body = await req.json();
if (!body.title || typeof body.title !== "string") {
return Response.json(
{ error: "Title is required" },
{ status: 400, headers }
);
}
const task = createTask(body);
return Response.json({ data: task }, { status: 201, headers });
}
// PUT /api/tasks/:id
if (taskMatch && method === "PUT") {
const body = await req.json();
const task = updateTask(Number(taskMatch[1]), body);
if (!task) {
return Response.json(
{ error: "Task not found" },
{ status: 404, headers }
);
}
return Response.json({ data: task }, { headers });
}
// DELETE /api/tasks/:id
if (taskMatch && method === "DELETE") {
const deleted = deleteTask(Number(taskMatch[1]));
if (!deleted) {
return Response.json(
{ error: "Task not found" },
{ status: 404, headers }
);
}
return Response.json({ message: "Task deleted" }, { headers });
}
return Response.json({ error: "Not Found" }, { status: 404, headers });
}7.5 : Point d'entrée
// src/index.ts
import { handleRequest } from "./router";
const server = Bun.serve({
port: Bun.env.PORT ? Number(Bun.env.PORT) : 3000,
fetch: handleRequest,
});
console.log(`Tasks API running at http://localhost:${server.port}`);Exécuter l'API
bun run src/index.tsTestez avec curl :
# Créer une tâche
curl -X POST http://localhost:3000/api/tasks \
-H "Content-Type: application/json" \
-d '{"title": "Apprendre Bun", "description": "Compléter le tutoriel Bun"}'
# Lister toutes les tâches
curl http://localhost:3000/api/tasks
# Mettre à jour une tâche
curl -X PUT http://localhost:3000/api/tasks/1 \
-H "Content-Type: application/json" \
-d '{"completed": true}'
# Supprimer une tâche
curl -X DELETE http://localhost:3000/api/tasks/1Étape 8 : Le test runner Bun
Bun inclut un test runner compatible Jest avec des assertions intégrées.
Écrire des tests
// tests/tasks.test.ts
import { describe, it, expect, beforeAll } from "bun:test";
const BASE_URL = "http://localhost:3000";
let createdTaskId: number;
beforeAll(async () => {
// Démarrer le serveur pour les tests
const module = await import("../src/index");
});
describe("Tasks API", () => {
it("should create a task", async () => {
const res = await fetch(`${BASE_URL}/api/tasks`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
title: "Test Task",
description: "A test task",
}),
});
expect(res.status).toBe(201);
const json = await res.json();
expect(json.data.title).toBe("Test Task");
createdTaskId = json.data.id;
});
it("should list all tasks", async () => {
const res = await fetch(`${BASE_URL}/api/tasks`);
expect(res.status).toBe(200);
const json = await res.json();
expect(json.data).toBeInstanceOf(Array);
expect(json.data.length).toBeGreaterThan(0);
});
it("should get a task by ID", async () => {
const res = await fetch(`${BASE_URL}/api/tasks/${createdTaskId}`);
expect(res.status).toBe(200);
const json = await res.json();
expect(json.data.id).toBe(createdTaskId);
});
it("should update a task", async () => {
const res = await fetch(`${BASE_URL}/api/tasks/${createdTaskId}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ completed: true }),
});
expect(res.status).toBe(200);
const json = await res.json();
expect(json.data.completed).toBe(1);
});
it("should return 400 for missing title", async () => {
const res = await fetch(`${BASE_URL}/api/tasks`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
expect(res.status).toBe(400);
});
it("should return 404 for non-existent task", async () => {
const res = await fetch(`${BASE_URL}/api/tasks/99999`);
expect(res.status).toBe(404);
});
});Exécuter les tests
# Exécuter tous les tests
bun test
# Exécuter un fichier de test spécifique
bun test tests/tasks.test.ts
# Mode watch
bun test --watch
# Avec couverture
bun test --coverageLe test runner de Bun est considérablement plus rapide que Jest — les suites de tests typiques s'exécutent en millisecondes au lieu de secondes.
Étape 9 : Le bundler Bun
Bun inclut un bundler de qualité production qui remplace webpack, esbuild et Rollup.
Bundling basique
# Bundler un fichier pour le navigateur
bun build ./src/client.ts --outdir ./dist
# Bundler pour la compatibilité Node.js
bun build ./src/index.ts --target node --outdir ./dist
# Bundler pour Bun
bun build ./src/index.ts --target bun --outdir ./distAPI programmatique
// build.ts
const result = await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
target: "bun",
minify: true,
splitting: true,
sourcemap: "external",
});
if (!result.success) {
console.error("Build failed:");
for (const log of result.logs) {
console.error(log);
}
process.exit(1);
}
console.log(`Build complete: ${result.outputs.length} files`);Créer un exécutable autonome
L'une des fonctionnalités les plus puissantes de Bun est la compilation de votre application en un seul exécutable :
bun build ./src/index.ts --compile --outfile tasks-apiCela produit un binaire autonome qui inclut le runtime Bun. Vous pouvez le distribuer sur n'importe quelle machine sans avoir besoin d'installer Bun ou Node.js :
./tasks-api
# Tasks API running at http://localhost:3000Étape 10 : Serveur WebSocket
Bun dispose d'un support WebSocket de première classe intégré au serveur HTTP :
// ws-server.ts
Bun.serve({
port: 3001,
fetch(req, server) {
// Upgrader la requête HTTP en WebSocket
if (server.upgrade(req)) {
return; // Upgrade réussi
}
return new Response("WebSocket server", { status: 200 });
},
websocket: {
open(ws) {
console.log("Client connected");
ws.subscribe("chat");
},
message(ws, message) {
// Diffuser à tous les abonnés
ws.publish("chat", `User: ${message}`);
},
close(ws) {
console.log("Client disconnected");
ws.unsubscribe("chat");
},
},
});
console.log("WebSocket server running on ws://localhost:3001");L'implémentation WebSocket de Bun gère plus d'un million de messages par seconde — ce qui le rend idéal pour les applications temps réel.
Étape 11 : Exécution de scripts et package.json
Bun peut exécuter les scripts package.json beaucoup plus rapidement que npm :
{
"name": "bun-tasks-api",
"scripts": {
"dev": "bun --watch run src/index.ts",
"start": "bun run src/index.ts",
"build": "bun build src/index.ts --compile --outfile dist/tasks-api",
"test": "bun test",
"test:watch": "bun test --watch",
"lint": "bunx @biomejs/biome check ./src",
"db:seed": "bun run scripts/seed.ts"
}
}Notez le flag --watch — Bun dispose d'un file watching intégré avec hot reload, remplaçant des outils comme nodemon.
Utiliser bunx
bunx est l'équivalent Bun de npx — il exécute des paquets sans les installer globalement :
bunx create-next-app my-app
bunx prisma migrate dev
bunx @biomejs/biome check ./srcÉtape 12 : Déployer en production
Option A : Docker
Créez un Dockerfile minimal :
FROM oven/bun:2 AS base
WORKDIR /app
# Installer les dépendances
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile --production
# Copier le source
COPY src/ src/
# Exécuter
EXPOSE 3000
CMD ["bun", "run", "src/index.ts"]Construire et exécuter :
docker build -t bun-tasks-api .
docker run -p 3000:3000 bun-tasks-apiOption B : Binaire autonome
Compilez et déployez un seul binaire :
# Construire sur votre machine
bun build src/index.ts --compile --outfile tasks-api
# Copier sur le serveur et exécuter
scp tasks-api user@server:/opt/app/
ssh user@server "chmod +x /opt/app/tasks-api && /opt/app/tasks-api"Option C : Fly.io
# fly.toml
app = "bun-tasks-api"
[build]
dockerfile = "Dockerfile"
[http_service]
internal_port = 3000
force_https = true
[[vm]]
size = "shared-cpu-1x"
memory = "256mb"fly launch
fly deployBenchmarks de performance
Voici une comparaison entre Bun et Node.js pour les opérations courantes (plus bas est meilleur) :
| Opération | Node.js 22 | Bun 2.0 | Accélération |
|---|---|---|---|
| Installation paquets (Next.js) | 28s | 1.8s | 15x |
| Exécution TypeScript | 320ms | 45ms | 7x |
| Serveur HTTP (req/s) | 68 000 | 260 000 | 3.8x |
| Lecture fichier (1 Go) | 1.2s | 0.4s | 3x |
| Suite de tests (100 tests) | 4.5s | 0.8s | 5.6x |
| Bundle (app React) | 1.1s | 0.3s | 3.7x |
Ces chiffres proviennent de charges de travail réelles typiques. Vos résultats peuvent varier en fonction du matériel et de la complexité du projet.
Dépannage
Problèmes courants
Paquets Node.js avec des addons natifs :
Certains paquets npm avec des addons C++ peuvent ne pas fonctionner avec Bun. Consultez le tracker de compatibilité sur bun.sh/ecosystem pour les problèmes connus.
Utilisation mémoire : Bun utilise JavaScriptCore au lieu de V8. Le comportement mémoire diffère — certaines charges utilisent moins de mémoire, d'autres plus. Surveillez avec :
bun run --smol src/index.ts # Réduit l'utilisation mémoire au prix de quelques performancesAPI Node.js manquantes : Bun implémente la plupart des API Node.js, mais certains cas limites peuvent différer. Si vous rencontrez une incompatibilité, consultez la documentation Bun pour les solutions alternatives.
Prochaines étapes
Maintenant que vous maîtrisez les fondamentaux de Bun 2.0, explorez ces domaines :
- Framework Hono — Utilisez Hono sur Bun pour une API plus structurée avec middleware
- Framework Elysia — API type-safe avec inférence de types de bout en bout sur Bun
- Bun Shell — Scriptez les tâches système avec l'API shell intégrée de Bun
- SQLite avec Drizzle ORM — Combinez le SQLite natif de Bun avec Drizzle pour des requêtes type-safe
- Applications WebSocket — Construisez des systèmes de chat ou de notifications en temps réel
Conclusion
Bun 2.0 est un changement de paradigme pour le développement JavaScript. En consolidant le runtime, le gestionnaire de paquets, le bundler et le test runner dans un seul binaire, il élimine la complexité de la chaîne d'outils qui a pesé sur l'écosystème JavaScript pendant des années.
Dans ce guide, vous avez construit une API REST complète avec persistance SQLite, appris à utiliser le test runner Bun, bundlé du code pour la production et exploré les options de déploiement. Les gains de vitesse seuls rendent Bun digne d'intérêt pour les nouveaux projets, et sa compatibilité avec Node.js signifie que vous pouvez l'adopter progressivement.
Le paysage des runtimes JavaScript a évolué. Bun 2.0 mène la charge.
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

Vite 6 + React + TypeScript : créer une application web moderne de A à Z en 2026
Maîtrisez Vite 6 avec React et TypeScript — de la création du projet au déploiement. Ce guide complet couvre l'Environment API, le HMR ultra-rapide, le bundling optimisé, les tests avec Vitest et les meilleures pratiques de production.

Biome : Remplacer ESLint et Prettier par un outil unique ultra-rapide
Découvrez comment migrer d'ESLint + Prettier vers Biome, le linter et formateur Rust ultra-rapide. Configuration, règles personnalisées, intégration CI/CD et VS Code — tout en un seul outil.

Vitest et React Testing Library avec Next.js 15 : Le Guide Complet des Tests Unitaires en 2026
Apprenez à configurer et maîtriser Vitest avec React Testing Library dans un projet Next.js 15. Ce tutoriel couvre les tests de composants, hooks, Server Components, API routes et l'intégration CI/CD.