Afficher une image
Rendre l’interface plus visuelle et moderne avec une image d’illustration

Notions théoriques
Pourquoi ajouter une image ?
Les images permettent de :
- Rendre le quiz plus attrayant
- Mieux illustrer certaines notions de cybersécurité (ex. : phishing, ransomware)
- Stimuler la mémorisation visuelle
Où placer l’image à l'écran ?
Nous allons afficher l’image à gauche de la question, dans la carte déjà utilisée par les élèves.
Cela donnera une interface plus équilibrée :
| Colonne gauche | Colonne droite |
|---|---|
| Image | Texte + Réponses |
Où stocker les images dans le projet ?
3 solutions sont possibles pour stocker les images
- Dans Supabase (via le Storage intégré)
- Dans le dossier
/publicde votre projet Next.js (solution simple pour débuter) - Depuis une URL externe (attention à la fiabilité du lien)
Dans cette séance, nous allons utiliser la méthode la plus simple : stocker les images localement dans le dossier /public/images.
Pour cette séance, nous allons stocker les images localement dans le dossier /public/images.
Ce dossier est accessible directement depuis le navigateur :
- Exemple :
/images/phishing.pngsera visible à l’adressehttp://localhost:3000/images/phishing.png
Où trouver des images gratuites ?
Pour illustrer une question sur le phishing, nous allons télécharger une image libre de droits représentant une tentative d’hameçonnage (phishing email).
Étape 1 — Trouver des images sur un site libre de droits
Voici 2 possibilités fiables :
-
Unsplash
Rechercher :phishing,cybersecurity,hacker,email fraudExemple de lien : https://unsplash.com/s/photos/phishing
-
Pixabay
Rechercher :phishing,cyber attack,cybersecurityExemple de lien : https://pixabay.com/images/search/phishing/
Étape 2 — Télécharger des images
Prenons un exemple concret sur Pixabay :
- Rendez-vous sur : https://pixabay.com/vectors/phishing-fraud-hacking-online-fraud-6926470/

- Cliquez sur le bouton Download
- Choisissez une taille raisonnable (ex. : 640×457 PNG 44 kB)
- Cliquez sur le bouton Download
Étape 3 — Enregistrer les images dans votre projet
- Renommez l’image téléchargée en
phishing.png - Placez-la dans le dossier
/public/imagesde votre projet Next.js :
/public/images/phishing.png
Si le dossier /public/images n’existe pas, créez le dossier /images dans /public.

Étape 4 — Vérifier que les images fonctionnent
Lancez votre serveur local (npm run dev) et ouvrez cette URL dans votre navigateur :
http://localhost:3000/images/phishing.png

Trouvez, téléchargez et enregistrez des images pour vos autres questions.
Veillez à respecter les droits d’auteur et à utiliser des images libres de droits.
Le composant <Image />
Next.js propose un composant optimisé pour afficher les images :
import Image from 'next/image';
<Image
src="/images/phishing.png"
alt="Illustration"
width={300}
height={200}
/>
Disposition en 2 colonnes
Nous allons utiliser Flexbox avec Tailwind CSS pour afficher l’image à gauche et le texte à droite :
<div className="flex">
<div className="w-1/2">Image</div>
<div className="w-1/2">Texte + Réponses</div>
</div>
TP pour réfléchir et résoudre des problèmes
Nous allons modifier l’application Quiz Cyber pour :
- Ajouter une image à une question
- Afficher cette image à gauche de la carte
- Respecter les droits d’auteur (nom de l’auteur et lien vers la source)
- Utiliser les composants
Image,Card,Alert, etc.
Avant de commencer, pensez à créer une nouvelle branche Git !
Étape 1 — Ajouter des colonnes
Ajouter des colonnes dans Supabase
- Allez dans Supabase > Table Editor
- A droite de la table
question, cliquez sur les 3 points...

- Cliquez sur Edit Table

- Cliquez sur le bouton Add column
- Saisissez les informations des nouvelles colonnes :
| Nom de la colonne | Type |
|---|---|
image_url | text |
image_credit_nom | text |
image_credit_url | text |

- Cliquez sur le bouton Save

-
Ensuite, modifiez une question existante pour y ajouter ces informations :
-
image_url:/images/phishing.png -
image_credit_nom:Mohamed Hassan -
image_credit_url:https://pixabay.com/users/mohamed_hassan-5229782/
Appuyez sur la touche Entrée pour enregistrer chaque modification.
Enregistrez les informations des images pour vos autres questions.

Étape 2 — Energistrer vos images
Ajouter vos images dans le dossier
public/images
- Téléchargez les image libre de droits (par exemple sur Pixabay)
- Placez-les dans le dossier
/public/images/ - Vérifiez que les images sont bien accessibles dans votre navigateur :
http://localhost:3000/images/phishing.png
Étape 3 — Utiliser <Image />
Afficher l’image de manière optimisée dans l’interface avec le composant
<Image />
Dans Next.js, le composant Image permet d’optimiser le chargement des images.
Voici un exemple d’utilisation :
import Image from "next/image";
<Image
src="/images/phishing.png"
alt="Illustration"
width={400}
height={300}
className="rounded"
/>
Le chemin src correspond à une image placée dans le dossier /public.
Étape 4 — Affichage en 2 colonnes
Organiser l’affichage en 2 colonnes pour afficher l’image à gauche et la question à droite
Utilisez Flexbox avec Tailwind CSS pour créer une disposition en 2 colonnes :
<div className="flex">
<div className="w-1/2">Image</div>
<div className="w-1/2">Texte + Réponses</div>
</div>
Chaque colonne occupe 50 % de la largeur de la carte.
Étape 5 — Afficher les droits d’auteur
Afficher les crédits de l’image pour respecter les droits d’auteur avec un lien cliquable
Utilisez les composants Alert et AlertDescription pour afficher les crédits sous l’image :
import { Alert, AlertDescription } from "@/components/ui/alert";
import Link from "next/link";
<Alert className="mt-4 text-sm text-muted-foreground">
<AlertDescription>
Image :{" "}
<Link
href="https://pixabay.com/users/mohamed_hassan-5229782/"
target="_blank"
rel="noopener noreferrer"
className="underline underline-offset-2 hover:text-primary"
>
Mohamed Hassan
</Link>
</AlertDescription>
</Alert>
Ce bloc s’affiche uniquement si les champs image_credit_nom et image_credit_url sont renseignés.
Maintenant que votre nouvelle fonctionnalité est terminée, pensez à envoyer votre travail vers GitHub !
Code complet
Voici le code complet à placer dans le fichier app/page.tsx :
"use client";
import { useEffect, useState } from "react";
import { supabase } from "../lib/supabaseClient";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import Image from "next/image";
import Link from "next/link";
export default function Home() {
const [question, setQuestion] = useState<any>(null);
useEffect(() => {
async function fetchQuestion() {
const { data, error } = await supabase
.from("question")
.select(`
id,
texte,
image_url,
image_credit_nom,
image_credit_url,
reponses:reponse (
id,
texte,
est_correcte
)
`)
.order("id", { ascending: true });
if (error) {
console.error("Erreur Supabase :", error);
} else {
setQuestion(data?.[0]); // On prend la première question du tableau
}
}
fetchQuestion();
}, []);
function handleClick(reponse: any) {
if (reponse.est_correcte) {
alert("Bonne réponse !");
} else {
alert("Mauvaise réponse.");
}
}
return (
<div>
<Alert className="bg-blue-50 border-blue-300 text-blue-800 max-w-xl mx-auto mt-6">
<AlertTitle className="text-xl font-semibold">Bienvenue sur CyberQuiz</AlertTitle>
<AlertDescription>
Un quiz pour tester vos connaissances en cybersécurité.
</AlertDescription>
</Alert>
{question ? (
<Card className="max-w-4xl mx-auto mt-6">
<div className="flex">
{/* Colonne gauche : image + crédit */}
<div className="w-1/2 p-4">
{question.image_url ? (
<Image
src={question.image_url}
alt="Illustration de la question"
width={400}
height={300}
className="rounded"
/>
) : (
<div className="w-full h-[300px] bg-gray-100 flex items-center justify-center text-sm text-gray-500 rounded">
Aucune image disponible
</div>
)}
{question.image_credit_nom && question.image_credit_url && (
<Alert className="mt-4 text-sm text-muted-foreground">
<AlertDescription>
<span className="inline">
Image :{" "}
<Link
href={question.image_credit_url}
target="_blank"
rel="noopener noreferrer"
className="underline underline-offset-2 hover:text-primary"
>
{question.image_credit_nom}
</Link>
</span>
</AlertDescription>
</Alert>
)}
</div>
{/* Colonne droite : question + réponses */}
<div className="w-1/2 p-4">
<CardHeader className="p-0 mb-4">
<CardTitle>Question</CardTitle>
</CardHeader>
<CardContent className="p-0">
<p className="text-lg font-semibold mb-4">{question.texte}</p>
{question.reponses.map((reponse: any) => (
<Button
key={reponse.id}
onClick={() => handleClick(reponse)}
className="w-full justify-start mt-2"
variant="outline"
>
{reponse.texte}
</Button>
))}
</CardContent>
</div>
</div>
</Card>
) : (
<p className="text-center mt-6">Chargement de la question...</p>
)}
</div>
);
}
Résultat attendu
- Une carte contenant :
- Une image affichée à gauche
- Le texte de la question et les réponses à droite
- Le nom de l’auteur de l’image affiché sous l’image, avec un lien cliquable
- Une interface sobre et claire, utilisant les composants ShadCN

Bonnes pratiques
- Utiliser des images légères (moins de 200 Ko)
- Vérifier que les liens de crédit sont valides et sécurisés (
https) - Utiliser les composants fournis pour garantir l’homogénéité de l’interface