Questions d'Entretien Technique - Next.js 13/14/15 (App Router)
⚪ NÉCESSAIRE
1. Comment créer une nouvelle page dans Next.js (App Router) ?
Voir la réponse
Créer un fichier page.tsx dans un dossier /app/nom-page/, structure dossiers = routes
// app/about/page.tsx
export default function About() {
return <h1>À propos</h1>
}
2. Quelle est la différence entre un Server Component et un Client Component ?
Voir la réponse
Server = rendu serveur, pas de JS client. Client ('use client') = interactif, hooks React
// Server Component (par défaut)
export default async function Page() {
const data = await fetch('...')
return <div>{data}</div>
}
// Client Component
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
3. Quand devez-vous utiliser 'use client' ?
Voir la réponse
Pour l'interactivité : useState, useEffect, événements (onClick), Context, browser APIs
'use client'
import { useState, useEffect } from 'react'
export default function Interactive() {
const [value, setValue] = useState('')
useEffect(() => {
console.log('Mounted')
}, [])
return <input value={value} onChange={(e) => setValue(e.target.value)} />
}
4. Comment optimiser une image dans Next.js ?
Voir la réponse
Utiliser next/image, optimisation auto, lazy loading, WebP/AVIF, priority pour above fold
import Image from 'next/image'
export default function Hero() {
return (
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // Pour les images above the fold
/>
)
}
5. Comment changer le titre et meta tags d'une page ?
Voir la réponse
Export metadata object ou fonction generateMetadata dans page.tsx
// Metadata statique
export const metadata = {
title: 'Ma Page',
description: 'Description de ma page'
}
// Metadata dynamique
export async function generateMetadata({ params }) {
const product = await fetch(`/api/products/${params.id}`)
return {
title: product.name,
description: product.description
}
}
6. Où placer les fichiers statiques (logo, favicon) ?
Voir la réponse
Dossier /public, accessible via /fichier.png
import Image from 'next/image'
export default function Logo() {
return <Image src="/logo.png" alt="Logo" width={100} height={100} />
}
// Le fichier doit être dans public/logo.png
🟢 BASIQUE
1. Comment créer un layout partagé entre plusieurs pages ?
Voir la réponse
Créer layout.tsx dans le dossier, entoure les pages enfants avec {children}
// app/dashboard/layout.tsx
export default function DashboardLayout({ children }) {
return (
<div>
<nav>Navigation du dashboard</nav>
<main>{children}</main>
</div>
)
}
2. À quoi sert le fichier loading.tsx ?
Voir la réponse
UI de chargement automatique (Suspense), affiché pendant fetch de données
// app/loading.tsx
export default function Loading() {
return <div>Chargement...</div>
}
3. À quoi sert le fichier error.tsx ?
Voir la réponse
Error Boundary automatique, gère les erreurs des composants/pages enfants
// app/error.tsx
'use client'
export default function Error({ error, reset }) {
return (
<div>
<h2>Erreur: {error.message}</h2>
<button onClick={() => reset()}>Réessayer</button>
</div>
)
}
4. Comment créer une route dynamique (ex: /blog/[slug]) ?
Voir la réponse
Créer dossier [slug] avec page.tsx, accéder via params.slug
// app/blog/[slug]/page.tsx
export default function BlogPost({ params }: { params: { slug: string } }) {
return <h1>Article: {params.slug}</h1>
}
5. Comment fetcher des données dans un Server Component ?
Voir la réponse
async/await directement dans le composant avec fetch(), pas besoin de useEffect
export default async function Page() {
const res = await fetch('https://api.example.com/data')
const data = await res.json()
return <div>{data.title}</div>
}
6. Comment rendre un fetch non-caché (toujours à jour) ?
Voir la réponse
fetch(url, { cache: 'no-store' }) ou { next: { revalidate: 0 } }
const res = await fetch(url, { cache: 'no-store' })
// ou
const res = await fetch(url, { next: { revalidate: 0 } })
7. Comment créer une API Route (endpoint) dans App Router ?
Voir la réponse
Créer route.ts avec exports GET, POST, etc. (pas dans /pages/api)
// app/api/users/route.ts
import { NextResponse } from 'next/server'
export async function GET() {
return NextResponse.json({ users: [] })
}
export async function POST(request: Request) {
const body = await request.json()
return NextResponse.json({ success: true })
}
8. Quelle est la différence entre layout.tsx et template.tsx ?
Voir la réponse
Layout persiste entre navigations, Template se re-monte à chaque navigation
// layout.tsx - Persiste entre navigations
export default function Layout({ children }) {
return <div>{children}</div>
}
// template.tsx - Re-monte à chaque navigation
export default function Template({ children }) {
return <div>{children}</div>
}
9. Comment générer des pages statiques pour des routes dynamiques ?
Voir la réponse
Export generateStaticParams qui retourne array de params à pré-générer
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json())
return posts.map((post) => ({ slug: post.slug }))
}
export default function Page({ params }: { params: { slug: string } }) {
return <div>{params.slug}</div>
}
10. Comment forcer le rendu statique d'une page ?
Voir la réponse
Pas de cookies(), headers(), ou fetch non-caché → automatiquement statique
// ✅ Rendu statique (pas d'appels dynamiques)
export default async function Page() {
const data = await fetch('https://api.example.com/data')
return <div>{data.title}</div>
}
// ❌ Rendu dynamique (utilise cookies)
import { cookies } from 'next/headers'
export default function Page() {
const cookieStore = cookies()
return <div>Dynamic</div>
}
🟡 INTERMÉDIAIRE
1. Qu'est-ce qu'une Server Action et comment l'utiliser ?
Voir la réponse
Fonction 'use server' appelable depuis client, pour mutations, utilisable dans <form action={...}>
// app/actions.ts
'use server'
export async function createUser(formData: FormData) {
const name = formData.get('name')
// Logique de création
return { success: true }
}
// Utilisation
<form action={createUser}>
<input name="name" />
<button type="submit">Créer</button>
</form>
2. Comment revalider le cache d'une page après une mutation ?
Voir la réponse
revalidatePath('/chemin') dans Server Action ou API Route
'use server'
import { revalidatePath } from 'next/cache'
export async function updatePost() {
// Logique de mise à jour
revalidatePath('/blog')
}
3. Quelle est la différence entre revalidatePath et revalidateTag ?
Voir la réponse
Path = invalide un chemin. Tag = invalide tous les fetch avec ce tag (next: { tags: [...] })
// Fetch avec tag
fetch(url, { next: { tags: ['posts'] } })
// Revalidation par tag
'use server'
import { revalidateTag } from 'next/cache'
revalidateTag('posts')
// Revalidation par path
import { revalidatePath } from 'next/cache'
revalidatePath('/blog')
4. Comment afficher un loading progressif avec Suspense ?
Voir la réponse
Wrapper composant async dans <Suspense fallback={...}>, streaming du reste
import { Suspense } from 'react'
export default function Page() {
return (
<Suspense fallback={<div>Chargement...</div>}>
<AsyncComponent />
</Suspense>
)
}
5. Pourquoi ce code crash : useState() dans un Server Component ?
Voir la réponse
Server Component = pas de hooks/interactivité, ajouter 'use client' ou déplacer logique
// ❌ Erreur
export default function ServerComponent() {
const [state, setState] = useState(0) // Crash!
return <div>{state}</div>
}
// ✅ Solution
'use client'
export default function ClientComponent() {
const [state, setState] = useState(0)
return <div>{state}</div>
}
6. Comment accéder aux cookies dans un Server Component ?
Voir la réponse
Importer cookies() de next/headers, rend la page dynamique (SSR)
import { cookies } from 'next/headers'
export default async function Page() {
const cookieStore = cookies()
const token = cookieStore.get('token')
return <div>{token?.value}</div>
}
7. Comment accéder aux headers de la requête ?
Voir la réponse
Importer headers() de next/headers, rend la page dynamique
import { headers } from 'next/headers'
export default async function Page() {
const headersList = headers()
const userAgent = headersList.get('user-agent')
return <div>{userAgent}</div>
}
8. Comment créer des métadatas dynamiques (selon l'ID produit) ?
Voir la réponse
Export fonction generateMetadata({ params }) async dans page.tsx
9. Quelle est la différence entre redirect() et useRouter() ?
Voir la réponse
redirect() = Server Components/Actions. useRouter() = Client Components uniquement
// Server Component
import { redirect } from 'next/navigation'
if (!user) redirect('/login')
// Client Component
'use client'
import { useRouter } from 'next/navigation'
const router = useRouter()
router.push('/dashboard')
10. Comment optimiser un composant lourd (chart) ?
Voir la réponse
Dynamic import avec next/dynamic, ssr: false, lazy loading
11. Comment protéger une route avec authentification ?
Voir la réponse
Middleware avec vérification token/cookie, ou check dans layout/page + redirect
// middleware.ts
import { NextResponse } from 'next/server'
export function middleware(request) {
const token = request.cookies.get('token')
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
}
export const config = {
matcher: '/dashboard/:path*'
}
// Ou dans un layout/page
import { cookies } from 'next/headers'
import { redirect } from 'next/navigation'
export default async function ProtectedPage() {
const token = cookies().get('token')
if (!token) redirect('/login')
return <div>Contenu protégé</div>
}
12. À quoi sert middleware.ts à la racine ?
Voir la réponse
S'exécute avant requête, pour auth, redirects, rewrites, i18n, A/B testing
// middleware.ts
import { NextResponse } from 'next/server'
export function middleware(request) {
const token = request.cookies.get('token')
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
}
export const config = {
matcher: '/dashboard/:path*'
}
13. Comment gérer les routes parallèles (Parallel Routes) ?
Voir la réponse
Dossiers @nom, rendu simultané dans layout via slots ({nom}), dashboard/modals
// Structure
app/
└── dashboard/
├── layout.tsx
├── @analytics/
│ └── page.tsx
└── @team/
└── page.tsx
// app/dashboard/layout.tsx
export default function Layout({ analytics, team }) {
return (
<div>
{analytics}
{team}
</div>
)
}
14. Comment gérer les intercepting routes (modals) ?
Voir la réponse
Convention (.) ou (..), intercepte navigation soft, garde URL, modal + page séparée
// Structure
app/
├── photos/
│ ├── page.tsx
│ └── [id]/
│ └── page.tsx # /photos/123 (page complète)
└── @modal/
└── (.)photos/
└── [id]/
└── page.tsx # Modal intercepté
// app/layout.tsx
export default function Layout({ children, modal }) {
return (
<>
{children}
{modal}
</>
)
}
15. Comment configurer le cache de fetch par défaut ?
Voir la réponse
Dans fetch() avec { cache: 'force-cache' } (défaut) ou no-store, ou revalidate: X
// Cache par défaut (force-cache)
const data1 = await fetch('https://api.example.com/data')
// Pas de cache
const data2 = await fetch('https://api.example.com/data', {
cache: 'no-store'
})
// Revalidation toutes les 60 secondes
const data3 = await fetch('https://api.example.com/data', {
next: { revalidate: 60 }
})
// Avec tags pour revalidation ciblée
const data4 = await fetch('https://api.example.com/data', {
next: { tags: ['posts'] }
})
🔴 AVANCÉ
1. Expliquez les 4 types de cache dans Next.js 13+ ?
Voir la réponse
Request Memoization, Data Cache, Full Route Cache, Router Cache (client)
1. Request Memoization: Déduplique les fetch identiques
2. Data Cache: Cache les résultats de fetch
3. Full Route Cache: Cache le HTML généré
4. Router Cache: Cache côté client des routes visitées
2. Comment désactiver complètement le cache pour une route ?
Voir la réponse
Export export const dynamic = 'force-dynamic' ou utiliser cookies()/headers()
// Option 1
export const dynamic = 'force-dynamic'
// Option 2
import { cookies } from 'next/headers'
// L'utilisation de cookies() désactive le cache
3. Comment forcer une route en Edge Runtime ?
Voir la réponse
Export export const runtime = 'edge' dans page/route, limité mais ultra-rapide
export const runtime = 'edge'
export default function Page() {
return <div>Page en Edge Runtime</div>
}
4. Quelle est la différence entre generateStaticParams et ISR ?
Voir la réponse
StaticParams = pre-render au build. ISR = revalidate après build avec revalidate: X
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json())
return posts.map((post) => ({ slug: post.slug }))
}
5. Comment implémenter un système de streaming de données ?
Voir la réponse
Server Component async + Suspense boundaries, React streaming, améliore TTFB
6. Comment optimiser les waterfalls de fetch ?
Voir la réponse
Paralléliser avec Promise.all(), ou Suspense boundaries séparées pour streaming
7. Comment passer des données entre Server Component parent et enfant ?
Voir la réponse
Props directement, pas besoin de Context, fetch peut être dédupliqué automatiquement
8. Peut-on utiliser Context API dans Server Components ?
Voir la réponse
Non, Context = Client Component uniquement, mais props drilling simplifié côté serveur
9. Comment sécuriser une Server Action ?
Voir la réponse
Validation inputs (Zod), vérifier auth dans action, rate limiting, CSRF automatique
// app/actions.ts
'use server'
export async function createUser(formData: FormData) {
const name = formData.get('name')
// Logique de création
return { success: true }
}
// Utilisation
<form action={createUser}>
<input name="name" />
<button type="submit">Créer</button>
</form>
10. Comment gérer les formulaires avec Server Actions ?
Voir la réponse
<form action={serverAction}>, FormData automatique, progressive enhancement, useFormStatus
// app/actions.ts
'use server'
export async function createUser(formData: FormData) {
const name = formData.get('name')
// Logique de création
return { success: true }
}
// Utilisation
<form action={createUser}>
<input name="name" />
<button type="submit">Créer</button>
</form>
11. Quelle stratégie pour migrer de Pages Router vers App Router ?
Voir la réponse
Migration progressive, coexistence possible, route par route, adapter data fetching
12. Comment implémenter l'optimistic UI avec Server Actions ?
Voir la réponse
useOptimistic hook, update UI immédiat, rollback si erreur
// app/actions.ts
'use server'
export async function createUser(formData: FormData) {
const name = formData.get('name')
// Logique de création
return { success: true }
}
// Utilisation
<form action={createUser}>
<input name="name" />
<button type="submit">Créer</button>
</form>
13. Comment débugger les problèmes de cache Next.js ?
Voir la réponse
Vérifier force-dynamic, logs fetch, .next/cache, headers cache, désactiver si debug
14. Comment gérer l'internationalisation (i18n) dans App Router ?
Voir la réponse
Route [lang] manuelle, middleware détection, next-intl, dictionaries, generateStaticParams
15. Quand utiliser unstable_cache ?
Voir la réponse
Pour cacher résultats de DB queries ou calculs coûteux hors fetch, avec tags pour revalidation
16. Comment implémenter un système multi-tenant ?
Voir la réponse
Middleware détection subdomain, headers custom, DB isolation, cache par tenant
17. Comment optimiser les Core Web Vitals (LCP, CLS, INP) ?
Voir la réponse
next/image, next/font, streaming, Suspense, code splitting, preload critical
18. Comment gérer les variables d'environnement serveur vs client ?
Voir la réponse
NEXT_PUBLIC_ = client. Sans préfixe = serveur uniquement, jamais exposé
// .env
NEXT_PUBLIC_API_URL=https://api.example.com // Accessible côté client
DATABASE_URL=postgresql://... // Serveur uniquement
// Utilisation
const apiUrl = process.env.NEXT_PUBLIC_API_URL // Client OK
const dbUrl = process.env.DATABASE_URL // Serveur uniquement
19. Quelle est la différence entre notFound() et redirect() ?
Voir la réponse
notFound() = affiche not-found.tsx (404). redirect() = redirige vers URL
import { notFound } from 'next/navigation'
export default async function Page({ params }) {
const data = await fetch(`/api/posts/${params.id}`)
if (!data) notFound()
return <div>{data.title}</div>
}
// app/not-found.tsx
export default function NotFound() {
return <h1>404 - Page non trouvée</h1>
}
20. Comment implémenter du partial prerendering (PPR) ?
Voir la réponse
Feature expérimentale Next.js 14+, mix statique + dynamique dans même page, Suspense boundaries
21. Comment monitorer les Server Components en production ?
Voir la réponse
APM (DataDog), Sentry, logs structurés, instrumentation.ts, metrics custom
22. Comment gérer les timeouts de Server Components/Actions ?
Voir la réponse
Config runtime, déplacer vers queue si long, streaming pour UX, edge pour rapidité
23. Comment implémenter une recherche avec URL state (filtres) ?
Voir la réponse
useSearchParams (client) ou searchParams prop (server), useRouter pour update
'use client'
import { useSearchParams, useRouter } from 'next/navigation'
export default function Search() {
const searchParams = useSearchParams()
const router = useRouter()
const query = searchParams.get('q')
function updateSearch(value: string) {
const params = new URLSearchParams(searchParams)
params.set('q', value)
router.push(`?${params.toString()}`)
}
return <input value={query || ''} onChange={(e) => updateSearch(e.target.value)} />
}
24. Quelle stratégie de déploiement Next.js sur infrastructure custom ?
Voir la réponse
Docker output: 'standalone', containers, CDN assets, cache layers, monitoring
25. Comment gérer les race conditions avec Server Actions ?
Voir la réponse
useTransition, disabled state, optimistic updates, validation côté serveur
// app/actions.ts
'use server'
export async function createUser(formData: FormData) {
const name = formData.get('name')
// Logique de création
return { success: true }
}
// Utilisation
<form action={createUser}>
<input name="name" />
<button type="submit">Créer</button>
</form>