standards observés chez Semrush.

### 1. Architecture des dossiers (App Router)

Pour un blog propre et scalable, la séparation des préoccupations est essentielle.

Plaintext

```
/app
  /blog
    layout.tsx           # Layout global du blog (Navbar, Footer partagés)
    page.tsx             # Liste des articles (avec pagination/infinite scroll)
    /category
      /[category]
        page.tsx         # Page de filtre par catégorie
    /[slug]
      page.tsx           # Page dynamique générée pour chaque article MDX
  /components
    /mdx                 # Composants React injectés dans le MDX (ex: CustomImage, CodeBlock)
    /blog                # Composants UI (PostCard, SearchBar, CategoryFilter)
```

### 2. Gestion des Articles MDX (Pages Fixes)

Puisque le contenu d'un blog est statique, vous devez générer les pages au moment du build (SSG) pour des performances maximales.

- **Bonne pratique :** Utilisez la fonction `generateStaticParams` dans `app/blog/[slug]/page.tsx` pour pré-rendre toutes les URLs de vos articles MDX.
- **Compilation MDX :** Utilisez `next-mdx-remote` ou `contentlayer` pour lire vos fichiers `.mdx`, extraire le Frontmatter (titre, date, auteur) et compiler le contenu de manière sécurisée côté serveur.

### 3. Filtre par Catégories et Recherche (Search)

- **Catégories (Rendu Statique) :** Semrush utilise des URLs dédiées pour ses catégories (ex: `/blog/category/seo/`). C'est la meilleure approche pour le SEO. Vous devez créer une route `app/blog/category/[category]/page.tsx` et utiliser `generateStaticParams` pour pré-générer ces pages.
- **Recherche (Côté Client / Serveur) :**
  - Ne téléchargez pas les 2000 articles côté client.
  - Utilisez un composant client `<SearchBar />` qui modifie l'URL avec les paramètres de recherche (ex: `/blog?q=nextjs`).
  - Lisez ce paramètre dans votre `page.tsx` serveur via la prop `searchParams` pour filtrer les articles renvoyés, ou utilisez une API route de recherche allégée (ex: Algolia ou un index JSON généré au build) si vous avez une barre de recherche en autocomplétion.

### 4. Pagination et Lazy Loading (Performances)

Pour éviter de crasher le navigateur avec 2000 nœuds DOM :

- **Pagination vs Infinite Scroll :** Privilégiez une pagination classique (URLs `/blog?page=2`) ou un bouton "Charger plus" plutôt qu'un infinite scroll agressif. Le bouton "Charger plus" peut appeler une Server Action qui renvoie le lot suivant d'articles (par bloc de 10 ou 12).
- **Lazy Loading natif :** Utilisez impérativement le composant `<Image />` de Next.js. Il gère automatiquement le redimensionnement, le format WebP/AVIF et applique un `loading="lazy"`.
- **Code Splitting :** Chargez vos composants lourds (comme les modales ou les filtres complexes) dynamiquement avec `next/dynamic`.

### 5. L'Audit SEO (Inspiré par Semrush)

L'analyse du code de Semrush révèle plusieurs pratiques SEO indispensables que vous devez implémenter via l'API Metadata de Next.js :

- **Balises Sémantiques :** Semrush enveloppe chaque carte d'article dans une balise `<article>`, utilise des balises `<h3>` pour les titres des cartes, et une balise `<time>` pour les dates. Reproduisez ceci fidèlement.
- **Hreflang & Internationalisation :** Si votre blog est multilingue, indiquez les versions alternatives de la page via les balises `<link rel="alternate" hreflang="..." />`, comme le fait Semrush pour l'anglais, le français, l'allemand, etc.
- **JSON-LD (Données structurées) :** Semrush injecte un script de type `application/ld+json` pour définir un objet `BreadcrumbList` (Fil d'Ariane). Sur une page article, ajoutez également le schéma `Article` ou `BlogPosting` pour aider Google à comprendre le contenu.

### 6. Template Layout Expert (Exemple de structure)

Voici un squelette conceptuel de ce à quoi devrait ressembler votre `app/blog/[slug]/page.tsx` niveau expert :

TypeScript

```
import { notFound } from 'next/navigation';
import { getPostBySlug } from '@/lib/mdx';
import { MDXRemote } from 'next-mdx-remote/rsc';
import Breadcrumbs from '@/components/Breadcrumbs';
import ShareButtons from '@/components/ShareButtons';

export async function generateMetadata({ params }) {
  const post = getPostBySlug(params.slug);
  if (!post) return {};
  
  return {
    title: post.title,
    description: post.excerpt,
    alternates: {
      canonical: `https://monsite.com/blog/${params.slug}`,
    },
    // ... Meta OpenGraph, Twitter, etc.
  };
}

export default async function BlogPostPage({ params }) {
  const post = getPostBySlug(params.slug);
  
  if (!post) {
    notFound();
  }

  // Génération du JSON-LD pour le SEO
  const jsonLd = {
    "@context": "https://schema.org",
    "@type": "BlogPosting",
    "headline": post.title,
    "datePublished": post.date,
    "author": [{ "@type": "Person", "name": post.author }]
  };

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      
      <div className="max-w-4xl mx-auto px-4 py-8">
        <Breadcrumbs items={[{ label: 'Blog', href: '/blog' }, { label: post.title }]} />
        
        <article className="mt-8">
          <header className="mb-10 text-center">
            <h1 className="text-4xl font-bold">{post.title}</h1>
            <div className="mt-4 text-gray-500">
              Par {post.author} • <time dateTime={post.date}>{post.formattedDate}</time>
            </div>
          </header>

          {/* Lazy Loaded Hero Image via Next/Image */}
          <div className="prose prose-lg max-w-none">
            {/* Injection experte des composants custom dans le MDX */}
            <MDXRemote source={post.content} components={mdxComponents} />
          </div>
        </article>

        <aside className="mt-12 border-t pt-8">
           <ShareButtons url={`https://monsite.com/blog/${post.slug}`} />
        </aside>
      </div>
    </>
  );
}
```