Construire un Chatbot IA Local avec Ollama et Next.js : Guide Complet

Vos données ne quittent jamais votre machine. Dans ce tutoriel, vous allez construire un chatbot IA entièrement fonctionnel qui tourne uniquement sur votre matériel local avec Ollama et Next.js — sans clés API, sans services cloud, sans partage de données.
Ce que vous allez apprendre
À la fin de ce tutoriel, vous serez capable de :
- Installer et configurer Ollama pour exécuter des LLM localement
- Construire une interface de chat Next.js avec streaming en temps réel
- Intégrer Ollama avec le Vercel AI SDK pour une expérience de qualité production
- Ajouter la sélection de modèles pour que les utilisateurs puissent basculer entre les LLM
- Gérer les erreurs gracieusement quand Ollama ne fonctionne pas
- Comprendre les compromis entre IA locale et IA cloud
Prérequis
Avant de commencer, assurez-vous d'avoir :
- Node.js 20+ installé (
node --version) - Connaissances de base en React et TypeScript
- Un éditeur de code — VS Code ou Cursor recommandé
- 8 Go+ de RAM (16 Go recommandés pour les modèles plus grands)
- macOS, Linux ou Windows avec WSL2
Pourquoi exécuter l'IA localement ?
Les services d'IA cloud comme OpenAI et Anthropic sont puissants, mais ils comportent des compromis :
| Considération | IA Cloud | IA Locale (Ollama) |
|---|---|---|
| Confidentialité | Données envoyées à des serveurs tiers | Les données restent sur votre machine |
| Coût | Paiement par token | Gratuit après le téléchargement |
| Latence | Aller-retour réseau nécessaire | Accès direct au matériel |
| Disponibilité | Nécessite internet | Fonctionne hors ligne |
| Personnalisation | Limité aux modèles du fournisseur | Exécuter tout modèle ouvert |
Pour les outils internes, le traitement de données sensibles et les applications hors ligne, l'IA locale est le choix évident.
Étape 1 : Installer Ollama
Ollama est un runtime léger pour exécuter des modèles de langage localement. Il gère le téléchargement, la quantification et le service des modèles via une API simple.
macOS
brew install ollamaOu téléchargez depuis ollama.com.
Linux
curl -fsSL https://ollama.com/install.sh | shWindows
Téléchargez l'installateur depuis ollama.com ou utilisez WSL2 avec les instructions Linux.
Vérifier l'installation
ollama --versionDémarrez le serveur Ollama :
ollama serveCela démarre un serveur API local sur http://localhost:11434.
Étape 2 : Télécharger votre premier modèle
Ollama donne accès à des centaines de modèles open-source. Commençons avec Llama 3.2, le modèle compact et performant de Meta :
ollama pull llama3.2Cela télécharge le modèle de 3 milliards de paramètres (~2 Go). Pour une option plus légère :
ollama pull llama3.2:1bModèles recommandés pour 2026
| Modèle | Taille | Idéal pour |
|---|---|---|
llama3.2:1b | 700 Mo | Réponses rapides, machines à ressources limitées |
llama3.2 | 2 Go | Chat général, bon équilibre |
mistral | 4 Go | Raisonnement fort, multilingue |
qwen3:4b | 2,5 Go | Raisonnement en chaîne de pensée |
qwen2.5-coder:7b | 4,5 Go | Génération et revue de code |
Testez votre modèle dans le terminal :
ollama run llama3.2
>>> What is the capital of Tunisia?Vous devriez voir une réponse comme : "The capital of Tunisia is Tunis."
Étape 3 : Créer le projet Next.js
Maintenant construisons l'interface de chat. Créez un nouveau projet Next.js :
npx create-next-app@latest ollama-chat --typescript --tailwind --app --src-dir
cd ollama-chatInstallez les dépendances nécessaires :
npm install ai ollama-ai-provider @ai-sdk/reactVoici ce que fait chaque package :
ai— Le cœur du Vercel AI SDK avecstreamText,generateTextet plus encoreollama-ai-provider— Le fournisseur communautaire qui connecte l'AI SDK à Ollama@ai-sdk/react— Les hooks React commeuseChatpour construire des interfaces de chat
Étape 4 : Configurer le fournisseur Ollama
Créez une configuration client Ollama partagée :
// src/lib/ollama.ts
import { createOllama } from 'ollama-ai-provider';
export const ollama = createOllama({
baseURL: process.env.OLLAMA_BASE_URL ?? 'http://localhost:11434/api',
});
export const DEFAULT_MODEL = process.env.OLLAMA_DEFAULT_MODEL ?? 'llama3.2';Ajoutez les variables d'environnement :
# .env.local
OLLAMA_BASE_URL=http://localhost:11434/api
OLLAMA_DEFAULT_MODEL=llama3.2Étape 5 : Construire la route API de chat
C'est le cœur de notre application — une route API Next.js qui diffuse les réponses d'Ollama.
// src/app/api/chat/route.ts
import { streamText } from 'ai';
import { ollama, DEFAULT_MODEL } from '@/lib/ollama';
export const maxDuration = 60;
export async function POST(req: Request) {
try {
const { messages, model } = await req.json();
const result = await streamText({
model: ollama(model ?? DEFAULT_MODEL),
system: 'You are a helpful, concise assistant. Answer questions clearly and accurately.',
messages,
});
return result.toDataStreamResponse();
} catch (error) {
if (error instanceof Error && error.message.includes('ECONNREFUSED')) {
return new Response(
JSON.stringify({
error: 'Ollama is not running. Start it with: ollama serve',
}),
{ status: 503, headers: { 'Content-Type': 'application/json' } }
);
}
return new Response(
JSON.stringify({ error: 'An unexpected error occurred' }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
}Points clés :
maxDuration = 60donne à la route jusqu'à 60 secondes pour le streaming, important pour les modèles plus grandsstreamTextgère le protocole de streaming entre Ollama et le clienttoDataStreamResponse()convertit le flux au format attendu paruseChat- La gestion d'erreurs capture les échecs de connexion quand Ollama ne fonctionne pas
Étape 6 : Ajouter un endpoint pour les modèles
Permettez aux utilisateurs de voir quels modèles sont disponibles localement :
// src/app/api/models/route.ts
export async function GET() {
try {
const baseURL = process.env.OLLAMA_BASE_URL?.replace('/api', '')
?? 'http://localhost:11434';
const res = await fetch(`${baseURL}/api/tags`);
if (!res.ok) {
throw new Error('Failed to fetch models');
}
const data = await res.json();
const models = data.models.map((m: { name: string; size: number }) => ({
id: m.name,
label: m.name,
size: `${(m.size / 1e9).toFixed(1)}GB`,
}));
return Response.json({ models });
} catch {
return Response.json({ models: [], error: 'Ollama is not available' });
}
}Étape 7 : Construire le composant de chat
Maintenant la partie amusante — l'interface de chat. Créez le composant de chat principal :
// src/components/Chat.tsx
'use client';
import { useChat } from '@ai-sdk/react';
import { useState, useRef, useEffect } from 'react';
import { ModelSelector } from './ModelSelector';
export function Chat() {
const [model, setModel] = useState('llama3.2');
const scrollRef = useRef<HTMLDivElement>(null);
const { messages, input, handleInputChange, handleSubmit, isLoading, error } =
useChat({
api: '/api/chat',
body: { model },
});
useEffect(() => {
scrollRef.current?.scrollTo({
top: scrollRef.current.scrollHeight,
behavior: 'smooth',
});
}, [messages]);
return (
<div className="flex flex-col h-screen max-w-3xl mx-auto">
{/* En-tête */}
<header className="flex items-center justify-between p-4 border-b">
<h1 className="text-xl font-semibold">Chat IA Local</h1>
<ModelSelector value={model} onChange={setModel} />
</header>
{/* Messages */}
<div ref={scrollRef} className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.length === 0 && (
<div className="text-center text-gray-500 mt-20">
<p className="text-4xl mb-4">🤖</p>
<p className="text-lg font-medium">Votre assistant IA privé</p>
<p className="text-sm mt-2">
Propulsé par Ollama — tout tourne sur votre machine.
</p>
</div>
)}
{messages.map((m) => (
<div
key={m.id}
className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-[80%] rounded-2xl px-4 py-3 ${
m.role === 'user'
? 'bg-blue-600 text-white'
: 'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100'
}`}
>
<p className="whitespace-pre-wrap">{m.content}</p>
</div>
</div>
))}
{isLoading && messages[messages.length - 1]?.role === 'user' && (
<div className="flex justify-start">
<div className="bg-gray-100 dark:bg-gray-800 rounded-2xl px-4 py-3">
<span className="animate-pulse">Réflexion en cours...</span>
</div>
</div>
)}
</div>
{/* Affichage des erreurs */}
{error && (
<div className="mx-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg text-red-700 dark:text-red-400 text-sm">
{error.message.includes('503')
? "Ollama ne fonctionne pas. Démarrez-le avec : ollama serve"
: "Quelque chose s'est mal passé. Veuillez réessayer."}
</div>
)}
{/* Saisie */}
<form onSubmit={handleSubmit} className="p-4 border-t">
<div className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="Tapez un message..."
disabled={isLoading}
className="flex-1 rounded-xl border px-4 py-3 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:border-gray-700"
/>
<button
type="submit"
disabled={isLoading || !input.trim()}
className="rounded-xl bg-blue-600 px-6 py-3 text-white font-medium hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
Envoyer
</button>
</div>
</form>
</div>
);
}Étape 8 : Construire le sélecteur de modèles
Ce composant récupère les modèles disponibles depuis Ollama et permet aux utilisateurs de basculer entre eux :
// src/components/ModelSelector.tsx
'use client';
import { useState, useEffect } from 'react';
interface Model {
id: string;
label: string;
size: string;
}
interface ModelSelectorProps {
value: string;
onChange: (model: string) => void;
}
export function ModelSelector({ value, onChange }: ModelSelectorProps) {
const [models, setModels] = useState<Model[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/models')
.then((res) => res.json())
.then((data) => {
setModels(data.models ?? []);
setLoading(false);
})
.catch(() => setLoading(false));
}, []);
if (loading) {
return (
<select disabled className="rounded-lg border px-3 py-2 text-sm opacity-50">
<option>Chargement des modèles...</option>
</select>
);
}
if (models.length === 0) {
return (
<span className="text-sm text-red-500">Aucun modèle trouvé</span>
);
}
return (
<select
value={value}
onChange={(e) => onChange(e.target.value)}
className="rounded-lg border px-3 py-2 text-sm bg-white dark:bg-gray-800 dark:border-gray-700"
>
{models.map((m) => (
<option key={m.id} value={m.id}>
{m.label} ({m.size})
</option>
))}
</select>
);
}Étape 9 : Relier la page
Mettez à jour la page principale pour afficher le composant de chat :
// src/app/page.tsx
import { Chat } from '@/components/Chat';
export default function Home() {
return <Chat />;
}Étape 10 : Lancer et tester
Démarrez le serveur de développement :
npm run devAssurez-vous qu'Ollama fonctionne dans un autre terminal :
ollama serveOuvrez http://localhost:3000 et commencez à discuter. Vous devriez voir :
- Le sélecteur de modèles rempli avec vos modèles locaux
- Des réponses en streaming en temps réel pendant que le modèle génère du texte
- Une expérience de chat fluide — tout tourne localement
Liste de vérification des tests
- Envoyez un message simple et vérifiez que le streaming fonctionne
- Basculez entre les modèles avec le sélecteur
- Arrêtez Ollama (
Ctrl+Csurollama serve) et vérifiez que le message d'erreur apparaît - Redémarrez Ollama et vérifiez que le chat récupère
- Envoyez un long message et vérifiez que le timeout de 60 secondes est suffisant
Aller plus loin : Sorties structurées
Ollama prend en charge les sorties JSON structurées avec des schémas Zod. C'est utile pour construire des outils, extraire des données ou imposer des formats de réponse :
// src/app/api/analyze/route.ts
import { generateObject } from 'ai';
import { ollama } from '@/lib/ollama';
import { z } from 'zod';
const SentimentSchema = z.object({
sentiment: z.enum(['positive', 'negative', 'neutral']),
confidence: z.number().min(0).max(1),
summary: z.string().max(200),
});
export async function POST(req: Request) {
const { text } = await req.json();
const { object } = await generateObject({
model: ollama('llama3.2'),
schema: SentimentSchema,
prompt: `Analyze the sentiment of this text: "${text}"`,
});
return Response.json(object);
}La réponse correspondra toujours à votre schéma :
{
"sentiment": "positive",
"confidence": 0.92,
"summary": "The text expresses strong satisfaction with the product."
}Aller plus loin : Embeddings pour le RAG
Vous pouvez utiliser Ollama pour générer des embeddings afin de construire un système de Génération Augmentée par Récupération (RAG) :
import { embedMany } from 'ai';
import { ollama } from '@/lib/ollama';
const { embeddings } = await embedMany({
model: ollama.embeddingModel('nomic-embed-text'),
values: [
'Next.js is a React framework for the web.',
'Ollama runs large language models locally.',
'TypeScript adds static types to JavaScript.',
],
});
// Chaque embedding est un tableau float32 que vous pouvez stocker dans une base vectorielle
console.log(embeddings[0].length); // 768 dimensionsCombinez cela avec une base de données vectorielle comme pgvector ou ChromaDB pour construire un pipeline RAG entièrement local.
Dépannage
Ollama ne répond pas
# Vérifiez si Ollama fonctionne
curl http://localhost:11434/api/tags
# Sinon, démarrez-le
ollama serveLe modèle est trop lent
Essayez un modèle plus petit :
ollama pull llama3.2:1b # 1 milliard de paramètres, beaucoup plus rapideOu vérifiez si votre machine supporte l'accélération GPU :
ollama ps # Affiche les modèles chargés et leur utilisation mémoireErreurs CORS dans le navigateur
N'appelez jamais Ollama directement depuis le navigateur. Passez toujours par votre route API Next.js — cela évite entièrement les problèmes de CORS et sécurise votre architecture.
Mémoire insuffisante
Les grands modèles nécessitent beaucoup de RAM. Si vous voyez des erreurs de mémoire :
- Utilisez une variante de modèle plus petite (
llama3.2:1bau lieu dellama3.2) - Fermez les autres applications gourmandes en mémoire
- Vérifiez la mémoire disponible avec
ollama ps
Vue d'ensemble de l'architecture
Voici comment les pièces s'assemblent :
┌─────────────────┐ HTTP POST ┌──────────────────┐ HTTP POST ┌──────────────┐
│ │ ──────────────► │ │ ──────────────► │ │
│ React Client │ /api/chat │ Next.js Server │ localhost:11434 │ Ollama │
│ (useChat) │ ◄────────────── │ (Route Handler) │ ◄────────────── │ (Local) │
│ │ SSE stream │ │ NDJSON stream │ │
└─────────────────┘ └──────────────────┘ └──────────────┘
- Le client React utilise le hook
useChatpour envoyer des messages et recevoir les réponses en streaming - La route API Next.js reçoit la requête, appelle Ollama via le fournisseur AI SDK et diffuse la réponse
- Ollama exécute l'inférence du modèle localement et renvoie les tokens en JSON délimité par des retours à la ligne
Toute la communication reste sur votre réseau local — rien n'atteint internet.
Prochaines étapes
Maintenant que vous avez un chatbot IA local fonctionnel, considérez ces améliorations :
- Ajouter un historique de conversation — Persistez les chats avec le stockage local ou une base de données
- Construire un pipeline RAG — Utilisez les embeddings et une base vectorielle pour le Q&A documentaire
- Ajouter l'appel d'outils — Laissez le modèle exécuter des fonctions comme la recherche web ou les calculs
- Déployer sur votre réseau local — Rendez le chatbot accessible aux autres appareils de votre réseau
- Essayer les modèles de vision — Utilisez
llama3.2-visionpour analyser des images localement
Tutoriels associés sur Noqta :
- Construire un système RAG agentique avec Next.js
- Construire votre premier serveur MCP avec TypeScript
- Drizzle ORM avec Next.js
Conclusion
Vous avez construit un chatbot IA entièrement local qui :
- Fonctionne entièrement sur votre matériel sans aucune dépendance cloud
- Diffuse les réponses en temps réel pour une expérience utilisateur fluide
- Prend en charge plusieurs modèles grâce à un sélecteur dynamique
- Gère les erreurs gracieusement quand Ollama est indisponible
- Utilise le Vercel AI SDK pour une architecture de qualité production
L'écosystème de l'IA locale a considérablement mûri en 2026. Avec Ollama qui gère le runtime des modèles et le Vercel AI SDK qui fournit l'expérience développeur, construire des applications d'IA privées est désormais aussi simple que construire n'importe quelle autre application web.
Vos données restent les vôtres. Votre IA tourne selon vos règles.
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

Construire un Agent IA Autonome avec Agentic RAG et Next.js
Apprenez a construire un agent IA qui decide de maniere autonome quand et comment recuperer des informations depuis des bases de donnees vectorielles. Un guide pratique complet avec Vercel AI SDK et Next.js, accompagne d'exemples executables.

Construire des agents IA from scratch avec TypeScript : maîtriser le pattern ReAct avec le Vercel AI SDK
Apprenez à construire des agents IA depuis zéro avec TypeScript. Ce tutoriel couvre le pattern ReAct, l'appel d'outils, le raisonnement multi-étapes et les boucles d'agents prêtes pour la production avec le Vercel AI SDK.

Authentifier votre application Next.js 15 avec Auth.js v5 : Email, OAuth et contrôle des rôles
Apprenez à ajouter une authentification prête pour la production à votre application Next.js 15 avec Auth.js v5. Ce guide complet couvre Google OAuth, les identifiants email/mot de passe, les routes protégées, le middleware et le contrôle d'accès basé sur les rôles.