Theming et personnalisation
Sur cette page
Leçon réécrite le 3 novembre 2026. La version précédente s’appuyait sur des composants et des options qui n’existent pas dans ChatKit :
ChatProvider,MessageInput, rendu personnalisé des messages, feuille de style injectée dans le chat. Elle suit désormais le guide Theming and customization d’OpenAI et les types de@openai/chatkit1.9.0 ; l’exemple complet a été vérifié par compilation TypeScript. Deux noms diffèrent entre le guide et le paquet publié, et la leçon suit le paquet :label(et nonname) pour les suggestions de l’écran d’accueil,leftActionetrightAction(et noncustomButtonLeftetcustomButtonRight) pour les boutons de l’en-tête. Sources : Theming and customization in ChatKit · référenceChatKitOptions· ChatKit Studio.
Un seul objet d’options
Toute la personnalisation de ChatKit passe par un objet d’options. En React, vous le passez à useChatKit({...}) ; avec l’élément web, à chatkit.setOptions({...}). Sa forme est la même dans les deux cas : ce que vous apprenez ici vaut pour toutes les intégrations.
Une conséquence découle de l’architecture vue à la leçon précédente : ChatKit s’affiche dans une iframe, et votre feuille de style ne l’atteint pas. Vos règles CSS s’appliquent au conteneur, la taille et la position que vous donnez à <ChatKit className="…" /> ; tout le reste se règle par les options. C’est une bonne nouvelle pour la maintenance : une mise à jour de ChatKit ne peut pas casser des sélecteurs CSS qui n’existent pas.
Pour essayer les options sans écrire de code, le bac à sable de ChatKit Studio affiche le rendu en direct.
Le thème
Le thème règle l’apparence générale : schéma de couleurs, couleur d’accent, arrondis, densité et typographie.
const { control } = useChatKit({
api: { url: '/chatkit', domainKey: 'local-dev' },
theme: {
colorScheme: 'dark',
color: {
accent: { primary: '#2D8CFF', level: 2 },
},
radius: 'round',
density: 'compact',
typography: { fontFamily: "'Inter', sans-serif", baseSize: 16 },
},
});
Chaque réglage a un jeu de valeurs fermé, que TypeScript vérifie pour vous :
| Option | Valeurs | Effet |
|---|---|---|
colorScheme | 'light', 'dark' | Thème clair (par défaut) ou sombre |
color.accent | primary (hexadécimal, rgb(), hsl()…), level de 0 à 3 | Couleur d’accent de l’interface et intensité de sa palette |
color.grayscale | hue en degrés, tint de 0 à 9, shade de −4 à 4 | Teinte et nuance de la palette de gris |
color.surface | background, foreground | Couleurs des surfaces : fond et premier plan (texte, icônes) |
radius | 'pill', 'round', 'soft', 'sharp' | Arrondi des bulles, boutons et champs |
density | 'compact', 'normal', 'spacious' | Espacement général |
typography | fontFamily, fontFamilyMono, baseSize (14 à 18), fontSources | Polices et taille de base ; fontSources charge une police web (family, src, weight…) |
Pour suivre la préférence de l’utilisateur, choisissez le schéma au moment de construire les options, par exemple avec window.matchMedia('(prefers-color-scheme: dark)').matches. Un theme réduit à une chaîne, theme: 'dark', est aussi accepté quand vous ne réglez que le schéma de couleurs.
L’écran d’accueil
Un fil vide est intimidant. L’écran d’accueil le remplace par un message et des suggestions, qui montrent à l’utilisateur ce que l’assistant sait faire :
startScreen: {
greeting: 'Bonjour ! Comment puis-je vous aider ?',
prompts: [
{ label: 'Suivre une commande', prompt: 'Où en est ma dernière commande ?', icon: 'search' },
{ label: 'Ouvrir un ticket', prompt: 'Je voudrais signaler un problème.', icon: 'write' },
],
},
label est le texte du bouton, prompt le message envoyé quand l’utilisateur clique, et icon un nom pris dans la liste des icônes de ChatKit (search, write, calendar, lightbulb…), ou une icône Lucide sous la forme 'lucide:nom'.
La zone de saisie
L’objet composer règle le texte d’invite, les outils proposés à l’utilisateur et les pièces jointes :
composer: {
placeholder: 'Posez votre question sur votre commande…',
tools: [
{ id: 'recherche-commande', label: 'Rechercher une commande', icon: 'search', pinned: true },
],
attachments: {
enabled: true,
maxCount: 3,
maxSize: 20 * 1024 * 1024, // 20 Mo par fichier
accept: { 'application/pdf': ['.pdf'], 'image/*': ['.png', '.jpg'] },
},
},
L’outil choisi dans le menu est transmis au serveur comme une préférence d’outil pour le tour suivant ; à vous d’en tenir compte dans votre agent.
Les pièces jointes sont désactivées par défaut, et les activer demande du travail côté serveur. Avec votre propre serveur, il faut aussi indiquer dans api comment les fichiers sont envoyés : uploadStrategy: { type: 'direct', uploadUrl: '…' } (l’interface poste le fichier à votre point d’accès) ou { type: 'two_phase' } (elle demande une URL signée, puis dépose le fichier dans votre stockage). Côté Python, vous fournissez alors un AttachmentStore. Ne cochez pas enabled: true tant que ce circuit n’existe pas.
L’en-tête, l’historique et les réponses
header: {
title: { text: 'Assistant commandes' },
leftAction: { icon: 'settings-cog', onClick: () => ouvrirReglages() },
},
history: { enabled: true, showRename: true, showDelete: false },
threadItemActions: { feedback: true, retry: true },
disclaimer: { text: "Les réponses peuvent contenir des erreurs. [Nos conditions](/cgu)" },
headeraffiche un titre fixe (sinon le titre du fil en cours) et jusqu’à deux boutons à vous,leftActionetrightAction, avec une icône d’en-tête (settings-cog,home…) et une fonction.header: { enabled: false }masque tout l’en-tête quand votre application fournit déjà sa propre barre.historyactive le panneau des conversations passées, avec ou sans renommage et suppression. Désactivez-le quand la notion de fil n’a pas de sens pour vous, par exemple pour un assistant de support sans compte utilisateur.threadItemActions.feedbackaffiche les pouces haut et bas sous les réponses. Chaque clic arrive sur votre serveur, dans la méthodeadd_feedbackde votreChatKitServer(thread_id,item_ids,feedbackà'positive'ou'negative'). C’est la matière première de vos évaluations.retryajoute le bouton qui relance une réponse.disclaimeraffiche sous la zone de saisie un court texte en Markdown.
La langue
Par défaut, ChatKit prend la langue du navigateur. Si votre application a son propre réglage de langue, imposez-la avec locale: 'fr', ou une variante régionale comme 'fr-FR' ou 'fr-CA'. Chaque requête envoyée à votre serveur porte cette langue dans l’en-tête Accept-Language : utilisez-la pour faire répondre l’agent et traduire les messages de vos outils dans la même langue que l’interface.
Tout assembler
Voici une configuration complète, compilée telle quelle avec @openai/chatkit-react 1.6.1 :
import { ChatKit, useChatKit } from '@openai/chatkit-react';
export function AssistantCommandes({ ouvrirReglages }: { ouvrirReglages: () => void }) {
const sombre = window.matchMedia('(prefers-color-scheme: dark)').matches;
const { control } = useChatKit({
api: { url: '/chatkit', domainKey: 'local-dev' },
locale: 'fr',
theme: {
colorScheme: sombre ? 'dark' : 'light',
color: { accent: { primary: '#2D8CFF', level: 2 } },
radius: 'round',
density: 'normal',
},
startScreen: {
greeting: 'Bonjour ! Comment puis-je vous aider ?',
prompts: [
{ label: 'Suivre une commande', prompt: 'Où en est ma dernière commande ?', icon: 'search' },
{ label: 'Ouvrir un ticket', prompt: 'Je voudrais signaler un problème.', icon: 'write' },
],
},
composer: { placeholder: 'Posez votre question sur votre commande…' },
header: {
title: { text: 'Assistant commandes' },
leftAction: { icon: 'settings-cog', onClick: ouvrirReglages },
},
history: { enabled: true, showRename: true, showDelete: false },
threadItemActions: { feedback: true, retry: true },
});
return <ChatKit control={control} className="h-[640px] w-full max-w-md" />;
}
Points clés à retenir
- Toute la personnalisation passe par un seul objet d’options, identique en React (
useChatKit) et avec l’élément web (setOptions) - ChatKit s’affiche dans une iframe : votre CSS ne règle que le conteneur, le reste passe par
theme theme:colorScheme,color.accent,radius,density,typography, avec des valeurs fermées vérifiées par TypeScriptstartScreen(accueil et suggestions),composer(invite, outils, pièces jointes),header,history,threadItemActions,disclaimeretlocalecomplètent l’interface- Les pièces jointes et les retours utilisateur demandent un relais côté serveur :
uploadStrategyetAttachmentStore, méthodeadd_feedback