Cours Next.js · Module 1: Configuration de Next.js

Structure des répertoires et des fichiers dans Next.js (app/ vs pages/)

13 min de lecture
Dans cette leçon9

À Quantum Metropolis, les ingénieurs conçoivent des systèmes de transport avancés capables de fonctionner aussi bien sur les routes classiques que sur les nouvelles voies de téléportation quantique. De même, Next.js propose deux systèmes de routage parallèles : le Pages Router traditionnel et l'App Router moderne. Dans ce module, nous analyserons en profondeur les différences entre ces deux approches et comprendrons comment organiser les fichiers et les répertoires dans nos projets.

Deux philosophies de routage dans Next.js

Next.js reposait à l'origine sur un routage basé sur les fichiers du dossier pages/. Dans Next.js 13, un nouveau système de routage basé sur le dossier app/ a été introduit. Cette évolution ressemble au passage des systèmes de transport classiques à la téléportation quantique à Quantum Metropolis - la nouvelle technologie est plus avancée, mais les deux coexistent encore.

Pages Router : l'approche classique

Le Pages Router est le système de routage d'origine de Next.js. Il est simple, bien documenté et encore largement utilisé. Dans cette convention, chaque fichier JavaScript ou TypeScript du répertoire pages/ devient automatiquement une route de l'application.

1pages/
2├── index.js                # Route: /
3├── about.js                # Route: /about
4├── contact.tsx             # Route: /contact
5├── products/
6│   ├── index.js            # Route: /products
7│   └── [id].js             # Route: /products/:id (dynamic)
8└── api/
9    └── hello.js            # API Endpoint: /api/hello

Dans ce système :

  • Les fichiers correspondent directement aux chemins d'URL
  • Les segments de chemin dynamiques sont indiqués entre crochets [param]
  • Un répertoire spécial api/ est utilisé pour gérer les points d'accès API
  • Chaque fichier exporte par défaut un composant React ou une fonction de gestion d'API

App Router : l'approche moderne

L'App Router, introduit dans Next.js 13 et perfectionné dans la version 15, représente une nouvelle approche basée sur les React Server Components. C'est comme passer des véhicules traditionnels à la téléportation quantique - un changement fondamental dans la façon de concevoir les applications web.

1app/
2├── layout.tsx              # Main layout (required)
3├── page.tsx                # Route: /
4├── about/
5│   └── page.tsx            # Route: /about
6├── blog/
7│   ├── page.tsx            # Route: /blog
8│   ├── layout.tsx          # Layout specific to /blog and its sub-routes
9│   └── [slug]/
10│       └── page.tsx        # Route: /blog/:slug (dynamic)
11└── api/
12    └── users/
13        └── route.ts        # API Endpoint: /api/users

Dans l'App Router :

  • Seuls les répertoires définissent les routes, et des fichiers spéciaux à l'intérieur de ces répertoires remplissent des fonctions précises
  • Le fichier page.tsx rend le composant de page pour une route donnée
  • Le fichier layout.tsx définit un layout partagé pour toutes les pages du répertoire et de ses sous-répertoires
  • Les segments dynamiques utilisent toujours des crochets : [param]
  • Les points d'accès API sont créés à l'aide de fichiers route.ts

Nommage des fichiers et conventions dans l'App Router

L'App Router introduit la « convention plutôt que la configuration » avec une série de fichiers spéciaux qui ont des rôles précis. C'est comme les modules spécialisés des systèmes de Quantum Metropolis - chacun avec une fonction définie dans l'architecture globale.

Fichiers spéciaux de base :

  1. page.tsx : rend l'interface utilisateur de la route et rend le chemin accessible publiquement.

    1// app/dashboard/page.tsx
    2export default function DashboardPage() {
    3  return (
    4    <section>
    5      <h1>Quantum Metropolis Control Panel</h1>
    6      <p>Welcome to the command center!</p>
    7    </section>
    8  );
    9}
  2. layout.tsx : définit une interface partagée pour un segment et ses enfants.

    1// app/dashboard/layout.tsx
    2export default function DashboardLayout({
    3  children,
    4}: {
    5  children: React.ReactNode;
    6}) {
    7  return (
    8    <div className="dashboard-layout">
    9      <nav className="dashboard-sidebar">
    10        {/* Sidebar navigation */}
    11      </nav>
    12      <main className="dashboard-main">{children}</main>
    13    </div>
    14  );
    15}
  3. route.ts : crée un point d'accès API à l'aide de la nouvelle API de gestion des requêtes.

    1// app/api/status/route.ts
    2import { NextResponse } from 'next/server';
    3
    4export async function GET() {
    5  return NextResponse.json({
    6    status: 'online',
    7    systemsOperational: 15,
    8    time: new Date().toISOString()
    9  });
    10}
    11
    12export async function POST(request: Request) {
    13  const data = await request.json();
    14  // Processing data...
    15  return NextResponse.json({ success: true });
    16}
  4. loading.tsx : crée une interface de chargement avec React Suspense.

    1// app/dashboard/loading.tsx
    2export default function DashboardLoading() {
    3  return (
    4    <div className="dashboard-loading">
    5      <div className="spinner"></div>
    6      <p>Loading Metropolis system data...</p>
    7    </div>
    8  );
    9}
  5. error.tsx : crée une interface de gestion des erreurs avec React Error Boundary.

    1'use client'; // Error handling components must be client components
    2
    3import { useEffect } from 'react';
    4
    5export default function DashboardError({
    6  error,
    7  reset,
    8}: {
    9  error: Error;
    10  reset: () => void;
    11}) {
    12  useEffect(() => {
    13    // Log error to monitoring system
    14    console.error(error);
    15  }, [error]);
    16
    17  return (
    18    <div className="error-container">
    19      <h2>An error occurred in the Metropolis systems!</h2>
    20      <p>{error.message}</p>
    21      <button onClick={reset}>Try again</button>
    22    </div>
    23  );
    24}
  6. not-found.tsx : crée une interface pour une ressource introuvable (404).

    1// app/not-found.tsx
    2export default function NotFound() {
    3  return (
    4    <div className="not-found">
    5      <h2>Unknown Zone</h2>
    6      <p>No Quantum Metropolis module was found with that identifier.</p>
    7      <a href="/">Return to command center</a>
    8    </div>
    9  );
    10}

Fonctionnalités avancées de l'App Router

L'App Router introduit plusieurs fonctionnalités d'organisation avancées qui ressemblent aux systèmes de transport avancés de Quantum Metropolis - elles permettent des routes plus complexes et plus flexibles.

1. Route Groups (groupes de routes)

Les groupes de routes permettent d'organiser les fichiers sans affecter la structure de l'URL. Ils sont indiqués en encadrant le nom du répertoire entre parenthèses : (group-name).

1app/
2├── (marketing)/        # Route group (does not affect URL path)
3│   ├── about/
4│   │   └── page.tsx    # Route: /about
5│   └── contact/
6│       └── page.tsx    # Route: /contact
7└── (dashboard)/        # Another route group
8    ├── layout.tsx      # Shared layout for dashboard routes
9    ├── page.tsx        # Route: /
10    └── stats/
11        └── page.tsx    # Route: /stats

Les groupes de routes sont comme les quartiers de Quantum Metropolis - ils aident à organiser la structure de la ville, mais n'affectent pas les adresses.

2. Parallel Routes (routes parallèles)

Les routes parallèles permettent de rendre simultanément plusieurs pages dans la même vue, ce qui est utile pour les layouts complexes. Elles sont indiquées par un nom de slot précédé du signe « @ » : @slot-name.

1app/
2├── layout.tsx          # Main layout
3├── page.tsx            # Main page
4└── dashboard/
5    ├── layout.tsx      # Layout with multiple slots
6    │   // layout.tsx defines slots {children, @stats, @notifications}
7    ├── page.tsx        # Default content for the main slot
8    ├── @stats/
9    │   ├── page.tsx    # Content for the @stats slot
10    │   └── default.tsx # Required in Next.js 16 (e.g. returns null)
11    └── @notifications/
12        ├── page.tsx    # Content for the @notifications slot
13        └── default.tsx # Required in Next.js 16 (e.g. returns null)

Dans Next.js 16, chaque slot doit avoir un fichier default.tsx (par exemple qui retourne null ou appelle notFound()), sinon next build échoue.

1// app/dashboard/layout.tsx
2export default function DashboardLayout({
3  children,
4  stats,
5  notifications,
6}: {
7  children: React.ReactNode;
8  stats: React.ReactNode;
9  notifications: React.ReactNode;
10}) {
11  return (
12    <div className="dashboard-grid">
13      <main>{children}</main>
14      <aside className="stats-panel">{stats}</aside>
15      <section className="notifications-panel">{notifications}</section>
16    </div>
17  );
18}

Les routes parallèles sont comme les dimensions parallèles de Quantum Metropolis - différentes réalités accessibles simultanément.

3. Intercepting Routes (routes d'interception)

Elles permettent d'« intercepter » une route et de l'afficher différemment, ce qui est idéal pour les modales qui préservent le contexte de navigation. Elles sont indiquées par le nom du segment précédé du signe « (.) » : (.)name, (..)name ou (...)name.

1app/
2├── layout.tsx
3├── page.tsx            # Route: /
4└── dashboard/
5    ├── layout.tsx
6    ├── page.tsx        # Route: /dashboard
7    └── projects/
8        ├── layout.tsx       # Renders {children} and {modal}
9        ├── page.tsx         # Route: /dashboard/projects
10        ├── [id]/
11        │   └── page.tsx     # Route: /dashboard/projects/:id
12        ├── create/
13        │   └── page.tsx     # Route: /dashboard/projects/create (full page)
14        └── @modal/
15            ├── default.tsx  # Returns null when the modal is closed
16            └── (.)create/
17                └── page.tsx # Modal over the list when opened via a link

Quand tu suis un lien depuis la liste, Next.js affiche la page interceptée dans le slot @modal au-dessus de la liste ; après un rafraîchissement ou en ouvrant directement l'adresse, il affiche la page complète create/page.tsx.

Les routes d'interception sont comme les raccourcis quantiques de Quantum Metropolis - elles permettent un accès rapide aux ressources sans changer de contexte.

Organisation du code dans Next.js 16

Outre la structure de routage, Next.js 16 suggère des façons précises d'organiser le code. C'est comme la planification des quartiers à Quantum Metropolis - une bonne organisation augmente l'efficacité et la lisibilité.

Composants et ressources partagés

Les composants utilisés à plusieurs endroits doivent être organisés de manière cohérente. Voici la structure recommandée :

1src/
2├── app/                 # Application with App Router
3│   ├── layout.tsx
4│   └── page.tsx
5├── components/          # Shared components
6│   ├── ui/              # Basic UI components
7│   │   ├── Button.tsx
8│   │   └── Card.tsx
9│   └── features/        # Feature-specific components
10│       ├── dashboard/
11│       └── auth/
12├── lib/                 # Shared functions and classes
13│   ├── api/
14│   └── utils/
15├── hooks/               # Custom React hooks
16│   ├── useAuth.ts
17│   └── useLocalStorage.ts
18└── styles/              # Global styles
19    └── globals.css

Co-location vs. centralisation

Next.js 16 et l'App Router favorisent une approche de « co-location », où les fichiers liés à une route spécifique sont placés à proximité les uns des autres. C'est comme les micro-quartiers de Quantum Metropolis - tout ce dont tu as besoin est à portée de main.

1app/
2└── dashboard/
3    ├── page.tsx
4    ├── loading.tsx       # Loading for this route
5    ├── error.tsx         # Error handling for this route
6    ├── layout.tsx        # Layout for this route
7    ├── actions.ts        # Server Actions for this route
8    ├── components/       # Components specific to this route
9    │   ├── StatusCard.tsx
10    │   └── DashboardNav.tsx
11    └── styles/           # Styles specific to this route
12        └── dashboard.module.css

Comparaison de l'App Router et du Pages Router

Maintenant que nous avons exploré les deux systèmes de routage, comparons-les - tout comme les ingénieurs de Quantum Metropolis pourraient comparer le transport classique et la téléportation quantique.

Rendu

  1. Pages Router :

    • Par défaut, chaque page est pré-rendue en HTML (optimisation statique automatique), puis hydratée dans le navigateur
    • SSR et SSG disponibles via getServerSideProps et getStaticProps
    • Modèles de données relativement simples
  2. App Router :

    • Server Components par défaut
    • Choix entre Server et Client Components
    • Modèle de flux de données plus avancé
    • Rendu en streaming

Dans le Pages Router, une page obtient ses données grâce à une fonction exportée à côté du composant, par exemple getStaticProps, que Next.js appelle une seule fois, pendant le build :

1// pages/posts.js (Pages Router)
2export async function getStaticProps() {
3  const posts = await getPosts();
4  return { props: { posts } };
5}

La fonction getServerSideProps a la même forme, mais Next.js l'appelle à chaque requête.

État de chargement

  1. Pages Router :

    • Gestion manuelle de l'état de chargement
    • Utilise souvent des bibliothèques externes
  2. App Router :

    • Composants loading.tsx intégrés
    • Intégration avec React Suspense
    • Indicateurs de chargement automatiques

Gestion des erreurs

  1. Pages Router :

    • Gestion manuelle des erreurs
    • Composant général pour les erreurs 500
  2. App Router :

    • Composants error.tsx avec gestion granulaire des erreurs
    • Intégration avec React Error Boundary
    • Possibilité de récupérer / réessayer après une erreur

Layouts

  1. Pages Router :

    • Création manuelle des layouts avec le composant _app.js
    • Un seul layout pour toute l'application
    • Les layouts imbriqués demandent du travail supplémentaire
  2. App Router :

    • Système de layouts intégré via layout.tsx
    • Possibilité de créer des layouts imbriqués
    • Le partage de layout est simple et intuitif

Quand utiliser quel système de routage ?

Choisir entre l'App Router et le Pages Router ressemble au choix entre la téléportation quantique et le transport traditionnel à Quantum Metropolis - cela dépend des besoins spécifiques.

Utilise l'App Router quand :

  • Tu crées un nouveau projet de zéro
  • Tu veux utiliser les dernières fonctionnalités de React et Next.js
  • Tu as besoin de capacités avancées de layout et d'organisation
  • Tu tiens à de meilleures performances et au rendu en streaming
  • Tu veux utiliser les React Server Components

Utilise le Pages Router quand :

  • Tu as un projet existant basé sur le Pages Router
  • Tu as besoin d'un modèle mental plus simple
  • Tu utilises des bibliothèques qui ne sont pas encore compatibles avec l'App Router
  • Tu veux utiliser des patterns et des solutions tout prêts

Migration du Pages Router vers l'App Router

Si tu as un projet existant utilisant le Pages Router, tu peux migrer progressivement vers l'App Router. C'est comme moderniser les quartiers de Quantum Metropolis - tu n'as pas à tout reconstruire d'un coup.

Next.js 16 permet aux deux systèmes de coexister. Tu peux commencer à utiliser app/ pour les nouvelles fonctionnalités tout en gardant les pages existantes dans pages/.

1.
2├── app/                # New features using App Router
3│   ├── new-feature/
4│   │   └── page.tsx    # /new-feature
5│   └── layout.tsx
6└── pages/              # Existing pages using Pages Router
7    ├── index.js        # /
8    └── about.js        # /about

Notes importantes sur la migration :

  • Les routes dans app/ ont la priorité sur les routes dans pages/, mais une même adresse ne peut pas exister dans les deux dossiers : Next.js signale une erreur pendant le build
  • Les deux structures utilisent la même configuration Next.js
  • Tu peux déplacer progressivement les pages de pages/ vers app/

Bonnes pratiques d'organisation des fichiers

Quel que soit le système de routage choisi, il vaut la peine de suivre certaines bonnes pratiques :

  1. Structure orientée fonctionnalité : organise les composants et le code autour des fonctionnalités métier, plutôt que par types techniques.

    1src/
    2├── features/
    3│   ├── auth/
    4│   │   ├── components/
    5│   │   ├── hooks/
    6│   │   └── utils/
    7│   └── dashboard/
    8│       ├── components/
    9│       ├── hooks/
    10│       └── utils/
  2. Approche modulaire : traite chaque fonctionnalité comme un module isolé avec une API clairement définie.

  3. Conventions de nommage cohérentes : établis des conventions et respecte-les tout au long du projet.

    • Fichiers de composants : PascalCase (Button.tsx)
    • Hooks : camelCase avec le préfixe « use » (useAuth.ts)
    • Fonctions utilitaires : camelCase (formatDate.ts)
  4. Imports relatifs vs. absolus : utilise les alias d'import (@/) pour une meilleure lisibilité et un déplacement de fichiers plus facile.

  5. Co-location des tests : place les tests à proximité des composants testés.

    1src/
    2├── components/
    3│   ├── Button.tsx
    4│   └── Button.test.tsx

Résumé

La structure des répertoires et des fichiers dans Next.js 16 est comme l'architecture de Quantum Metropolis - avec une bonne planification, elle peut être à la fois élégante et fonctionnelle. L'App Router introduit un modèle d'organisation d'application plus avancé mais aussi plus flexible, tandis que le Pages Router offre une approche plus simple et plus directe.

Le choix entre les deux dépend des besoins spécifiques du projet, mais pour les nouveaux projets, il est recommandé d'utiliser l'App Router afin de profiter des dernières fonctionnalités et optimisations. Souviens-toi qu'une bonne organisation du projet est la clé d'une maintenance et d'une extensibilité faciles à l'avenir.

Dans la prochaine leçon, nous nous plongerons dans le système de composants de Next.js 16, en particulier les différences entre les Client Components et les Server Components, qui constituent une partie fondamentale de l'App Router.

Code de cette leçon : App.tsx
1// Props et State - Metropolis Quantum
2import React, { useState } from 'react';
3
4console.log("Props et State dans React");
5console.log("Gestion des données à Quantum City\n");
6
7// ==========================================
8// 1. PROPS - passer des données
9// ==========================================
10console.log("=== 1. PROPS - propriétés ===");
11
12interface UserCardProps {
13  name: string;
14  role: string;
15  level: number;
16  avatar: string;
17}
18
19const UserCard: React.FC<UserCardProps> = ({ name, role, level, avatar }) => {
20  return (
21    <div style={{
22      background: 'rgba(255, 255, 255, 0.1)',
23      padding: '20px',
24      borderRadius: '12px',
25      border: '2px solid #64ffda',
26      display: 'flex',
27      alignItems: 'center',
28      gap: '15px',
29      marginBottom: '15px'
30    }}>
31      <div style={{ fontSize: '3rem' }}>{avatar}</div>
32      <div>
33        <h3 style={{ color: '#64ffda', margin: '0 0 5px 0' }}>{name}</h3>
34        <p style={{ margin: '5px 0', color: '#b0bec5' }}>Rôle : {role}</p>
35        <p style={{ margin: '5px 0', color: '#ff9800' }}>Niveau : {level}</p>
36      </div>
37    </div>
38  );
39};
40
41// ==========================================
42// 2. STATE - état du composant
43// ==========================================
44console.log("\n=== 2. STATE - Hook useState ===");
45
46function QuantumCounter() {
47  // useState renvoie [valeur, fonction pour changer la valeur]
48  const [energy, setEnergy] = useState<number>(50);
49  const [isCharging, setIsCharging] = useState<boolean>(false);
50
51  const charge = () => {
52    if (energy < 100) {
53      setEnergy(energy + 10);
54      setIsCharging(true);
55      setTimeout(() => setIsCharging(false), 300);
56      console.log(`Chargement de l'énergie : ${energy + 10}%`);
57    }
58  };
59
60  const discharge = () => {
61    if (energy > 0) {
62      setEnergy(energy - 10);
63      console.log(`Déchargement de l'énergie : ${energy - 10}%`);
64    }
65  };
66
67  const reset = () => {
68    setEnergy(50);
69    console.log("Énergie réinitialisée à 50%");
70  };
71
72  const getEnergyColor = () => {
73    if (energy > 70) return '#4caf50';
74    if (energy > 30) return '#ff9800';
75    return '#f44336';
76  };
77
78  return (
79    <div style={{
80      background: 'rgba(124, 77, 255, 0.2)',
81      padding: '20px',
82      borderRadius: '12px',
83      border: '2px solid #7c4dff'
84    }}>
85      <h3 style={{ color: '#7c4dff' }}>Générateur d'énergie quantique</h3>
86
87      <div style={{
88        background: 'rgba(0, 0, 0, 0.5)',
89        padding: '15px',
90        borderRadius: '8px',
91        marginBottom: '15px'
92      }}>
93        <div style={{
94          fontSize: '2rem',
95          textAlign: 'center',
96          color: getEnergyColor(),
97          marginBottom: '10px'
98        }}>
99          {energy}%
100        </div>
101        <div style={{
102          width: '100%',
103          height: '20px',
104          background: 'rgba(255, 255, 255, 0.1)',
105          borderRadius: '10px',
106          overflow: 'hidden'
107        }}>
108          <div style={{
109            width: `${energy}%`,
110            height: '100%',
111            background: `linear-gradient(90deg, ${getEnergyColor()}, #64ffda)`,
112            transition: 'all 0.3s ease',
113            transform: isCharging ? 'scale(1.05)' : 'scale(1)'
114          }}></div>
115        </div>
116      </div>
117
118      <div style={{ display: 'flex', gap: '10px', justifyContent: 'center' }}>
119        <button
120          onClick={charge}
121          disabled={energy >= 100}
122          style={{
123            background: energy >= 100 ? '#555' : 'linear-gradient(45deg, #4caf50, #8bc34a)',
124            color: 'white',
125            border: 'none',
126            padding: '10px 20px',
127            borderRadius: '6px',
128            cursor: energy >= 100 ? 'not-allowed' : 'pointer',
129            fontWeight: 'bold'
130          }}
131        >
132          Charger
133        </button>
134
135        <button
136          onClick={discharge}
137          disabled={energy <= 0}
138          style={{
139            background: energy <= 0 ? '#555' : 'linear-gradient(45deg, #f44336, #e91e63)',
140            color: 'white',
141            border: 'none',
142            padding: '10px 20px',
143            borderRadius: '6px',
144            cursor: energy <= 0 ? 'not-allowed' : 'pointer',
145            fontWeight: 'bold'
146          }}
147        >
148          Décharger
149        </button>
150
151        <button
152          onClick={reset}
153          style={{
154            background: 'linear-gradient(45deg, #607d8b, #90a4ae)',
155            color: 'white',
156            border: 'none',
157            padding: '10px 20px',
158            borderRadius: '6px',
159            cursor: 'pointer',
160            fontWeight: 'bold'
161          }}
162        >
163          Réinitialiser
164        </button>
165      </div>
166    </div>
167  );
168}
169
170// ==========================================
171// 3. Props + State ensemble
172// ==========================================
173console.log("\n=== 3. Props + State combinés ===");
174
175interface SystemStatusProps {
176  systemName: string;
177  initialStatus: 'online' | 'offline' | 'maintenance';
178}
179
180const SystemStatus: React.FC<SystemStatusProps> = ({ systemName, initialStatus }) => {
181  const [status, setStatus] = useState(initialStatus);
182  const [uptime, setUptime] = useState(0);
183
184  const toggleStatus = () => {
185    const statuses: Array<'online' | 'offline' | 'maintenance'> = ['online', 'offline', 'maintenance'];
186    const currentIndex = statuses.indexOf(status);
187    const nextStatus = statuses[(currentIndex + 1) % statuses.length];
188    setStatus(nextStatus);
189    setUptime(0);
190    console.log(`Statut de ${systemName} changé en : ${nextStatus}`);
191  };
192
193  const getStatusColor = () => {
194    switch(status) {
195      case 'online': return '#4caf50';
196      case 'offline': return '#f44336';
197      case 'maintenance': return '#ff9800';
198    }
199  };
200
201  return (
202    <div style={{
203      background: 'rgba(255, 255, 255, 0.1)',
204      padding: '15px',
205      borderRadius: '10px',
206      border: `2px solid ${getStatusColor()}`,
207      marginBottom: '15px'
208    }}>
209      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
210        <div>
211          <h4 style={{ margin: '0 0 10px 0', color: '#64ffda' }}>{systemName}</h4>
212          <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
213            <div style={{
214              width: '12px',
215              height: '12px',
216              borderRadius: '50%',
217              background: getStatusColor(),
218              boxShadow: `0 0 10px ${getStatusColor()}`
219            }}></div>
220            <span style={{ color: getStatusColor(), textTransform: 'uppercase', fontWeight: 'bold' }}>
221              {status}
222            </span>
223          </div>
224        </div>
225        <button
226          onClick={toggleStatus}
227          style={{
228            background: 'linear-gradient(45deg, #2196f3, #21cbf3)',
229            color: 'white',
230            border: 'none',
231            padding: '8px 16px',
232            borderRadius: '6px',
233            cursor: 'pointer',
234            fontWeight: 'bold'
235          }}
236        >
237          Changer le statut
238        </button>
239      </div>
240    </div>
241  );
242};
243
244// ==========================================
245// App principale
246// ==========================================
247function App() {
248  const citizens = [
249    { name: 'Alex Quantum', role: 'Architecte en chef', level: 42, avatar: '' },
250    { name: 'Nova Star', role: 'Data Scientist', level: 38, avatar: '' },
251    { name: 'Cypher Neo', role: 'Expert en sécurité', level: 45, avatar: '' }
252  ];
253
254  return (
255    <div style={{
256      padding: '20px',
257      background: 'linear-gradient(135deg, #0f0f23 0%, #1a1a2e 100%)',
258      minHeight: '100vh',
259      color: 'white'
260    }}>
261      <h1 style={{ textAlign: 'center', color: '#64ffda' }}>Panneau de contrôle de la Metropolis</h1>
262
263      <div style={{ marginTop: '30px' }}>
264        <h2 style={{ color: '#7c4dff' }}>Citoyens (démo Props)</h2>
265        {citizens.map((citizen, index) => (
266          <UserCard
267            key={index}
268            name={citizen.name}
269            role={citizen.role}
270            level={citizen.level}
271            avatar={citizen.avatar}
272          />
273        ))}
274      </div>
275
276      <div style={{ marginTop: '30px' }}>
277        <h2 style={{ color: '#7c4dff' }}>Système d'énergie (démo State)</h2>
278        <QuantumCounter />
279      </div>
280
281      <div style={{ marginTop: '30px' }}>
282        <h2 style={{ color: '#7c4dff' }}>État des systèmes (Props + State)</h2>
283        <SystemStatus systemName="Quantum Core" initialStatus="online" />
284        <SystemStatus systemName="Neural Network" initialStatus="maintenance" />
285        <SystemStatus systemName="Transport Grid" initialStatus="offline" />
286      </div>
287
288      <div style={{
289        marginTop: '30px',
290        background: 'rgba(100, 255, 218, 0.1)',
291        padding: '20px',
292        borderRadius: '10px',
293        borderLeft: '4px solid #64ffda'
294      }}>
295        <h3>Props vs State :</h3>
296        <ul style={{ lineHeight: '1.8' }}>
297          <li><strong>Props</strong> : transmises par le parent, en lecture seule, comme des paramètres de fonction</li>
298          <li><strong>State</strong> : état interne du composant, modifiable, provoque un re-rendu du composant</li>
299          <li><strong>Props + State</strong> : le plus souvent utilisés ensemble pour une UI dynamique</li>
300        </ul>
301      </div>
302    </div>
303  );
304}
305
306export default App;

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Que signifie la « programmation déclarative » dans le contexte de React ?

  2. 2. Les composants React peuvent être :

Tâches pratiques dans le jeu

  • Tri horizontal

    Ordonne le chemin vers une page d'article dynamique dans Next.js

  • Éditeur de code

    Crée le composant de page About pour le fichier pages/about.js (export default function About). La page doit afficher un titre <h1> ou <h2> avec le nom de l'entreprise et un paragraphe <p> avec des informations sur l'entreprise.

Articles utiles