Skip to main content

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>