Découpler le front PrestaShop avec Next.js améliore drastiquement les performances et le contrôle SEO — à condition d'adopter la bonne architecture dès le départ. Ce guide couvre le câblage API, le rendu SSR/ISR et les points critiques pour que Google indexe correctement votre catalogue.
Pourquoi headless PrestaShop avec Next.js ?
PrestaShop expose depuis la version 1.7 une API REST complète (Webservices) et PrestaShop 9 introduit une API modulaire Symfony plus cohérente. Next.js, de son côté, combine le meilleur du rendu côté serveur (SSR) et de la génération statique (SSG/ISR), ce qui en fait un choix naturel pour les fronts e-commerce SEO-friendly.
Les bénéfices concrets de cette architecture :
- Performance : HTML pré-rendu, pas de JavaScript à exécuter avant le premier affichage
- SEO total : contrôle absolu des balises
<head>, canonical, structured data, sitemap - DX : stack React/TypeScript, hot reload, écosystème npm
- Scalabilité : front déployable sur Vercel/CDN, back PrestaShop isolé
Architecture cible
L'architecture se divise en deux domaines distincts :
┌─────────────────────────────────────────────────┐
│ Next.js (front) │ PrestaShop (back) │
│ shop.example.com │ api.example.com │
│ │ │
│ Pages SSR/ISR │ Webservices REST │
│ /catalogue/[slug] │ /api/products │
│ /produit/[slug] │ /api/categories │
│ │ /api/orders │
└─────────────────────────────────────────────────┘
Le front Next.js ne sert jamais de proxy — il consomme directement l'API PrestaShop côté serveur (dans les Server Components ou getServerSideProps). Le panier et le checkout peuvent rester sur PrestaShop natif ou être réécrits en React selon le budget.
Activer et sécuriser l'API PrestaShop
Dans Paramètres avancés → Webservices, activez l'API et créez une clé avec les permissions nécessaires en lecture :
// Ressources minimales pour un front catalogue
products → GET
categories → GET
product_images → GET
combinations → GET
stock_availables → GET
Côté Next.js, stockez la clé dans les variables d'environnement et créez un client HTTP dédié :
// lib/prestashop-client.ts
const PS_API_URL = process.env.PS_API_URL!;
const PS_API_KEY = process.env.PS_API_KEY!;
export async function fetchPs<T>(resource: string, params: Record<string, string> = {}): Promise<T> {
const url = new URL(`${PS_API_URL}/api/${resource}`);
url.searchParams.set('output_format', 'JSON');
url.searchParams.set('display', 'full');
for (const [key, value] of Object.entries(params)) {
url.searchParams.set(key, value);
}
const response = await fetch(url.toString(), {
headers: {
Authorization: `Basic ${Buffer.from(`${PS_API_KEY}:`).toString('base64')}`,
},
next: { revalidate: 3600 }, // ISR : revalide toutes les heures
});
if (!response.ok) {
throw new Error(`PS API error: ${response.status} on ${resource}`);
}
return response.json() as Promise<T>;
}
L'option next: { revalidate: 3600 } active l'Incremental Static Regeneration : Next.js met la réponse en cache et la régénère en arrière-plan toutes les heures, sans bloquer les utilisateurs.
Page produit SEO-friendly avec SSR
Pour les fiches produit, le SSR garantit que Google reçoit le HTML complet à chaque crawl, même si les données changent souvent (stock, prix) :
// app/produit/[slug]/page.tsx
import { fetchPs } from '@/lib/prestashop-client';
import type { Metadata } from 'next';
interface ProductPageProps {
params: { slug: string };
}
export async function generateMetadata({ params }: ProductPageProps): Promise<Metadata> {
const product = await getProductBySlug(params.slug);
return {
title: `${product.name} | Ma Boutique`,
description: product.description_short,
openGraph: {
title: product.name,
description: product.description_short,
images: [{ url: product.cover_image_url, width: 800, height: 800 }],
},
alternates: {
canonical: `https://shop.example.com/produit/${params.slug}`,
},
};
}
export default async function ProductPage({ params }: ProductPageProps) {
const product = await getProductBySlug(params.slug);
const structuredData = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
description: product.description_short,
image: product.cover_image_url,
offers: {
'@type': 'Offer',
price: product.price,
priceCurrency: 'EUR',
availability: product.quantity > 0
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
},
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
<ProductDetail product={product} />
</>
);
}
Génération statique du catalogue avec ISR
Pour les pages de catégorie et le listing produits, l'ISR est idéal : le contenu est statique 99 % du temps mais peut être régénéré automatiquement :
// app/catalogue/[category]/page.tsx
export async function generateStaticParams() {
const data = await fetchPs<{ categories: Array<{ link_rewrite: string }> }>('categories', {
filter: '[active]': '1',
});
return data.categories.map((category) => ({
category: category.link_rewrite,
}));
}
// Revalidation toutes les 2 heures
export const revalidate = 7200;
Next.js pré-génère toutes les pages de catégorie au build, puis les régénère en arrière-plan selon l'intervalle défini.
Sitemap dynamique
Next.js 13+ permet de générer un sitemap programmatiquement via un fichier app/sitemap.ts :
// app/sitemap.ts
import { MetadataRoute } from 'next';
import { fetchPs } from '@/lib/prestashop-client';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const BASE_URL = 'https://shop.example.com';
const productsData = await fetchPs<{ products: Array<{ link_rewrite: string; date_upd: string }> }>(
'products',
{ filter: '[active]': '1', limit: '1000' }
);
const productUrls = productsData.products.map((product) => ({
url: `${BASE_URL}/produit/${product.link_rewrite}`,
lastModified: new Date(product.date_upd),
changeFrequency: 'weekly' as const,
priority: 0.8,
}));
return [
{ url: BASE_URL, lastModified: new Date(), changeFrequency: 'daily', priority: 1 },
...productUrls,
];
}
Pièges courants à éviter
Quelques erreurs fréquentes rencontrées sur des projets headless PrestaShop :
- Utiliser
useEffectpour fetch les données produit : le contenu n'est pas dans le HTML initial, Google voit une page vide. Toujours fetcher dans les Server Components. - Oublier les balises canonical : si PrestaShop reste accessible en direct (
api.example.com/fr/produit/…), les deux URLs se font concurrence. Ajoutez des canonical côté PS ou bloquez avec robots.txt. - Exposer la clé API en frontend : toutes les requêtes à l'API PS doivent passer côté serveur. Ne jamais mettre
PS_API_KEYdansNEXT_PUBLIC_. - Ignorer le cache du panier : l'état du panier est côté client (localStorage ou session cookie). Il faut gérer la synchronisation avec l'API PrestaShop cart séparément.
Checklist SEO pour go-live
- Canonical sur toutes les pages (produit, catégorie, pagination)
- Balises
hreflangsi boutique multilingue - Structured data Product + BreadcrumbList + Organization
- Sitemap accessible sur
/sitemap.xmlet soumis à Google Search Console robots.txtbloquant l'accès direct au back PrestaShop- Core Web Vitals : LCP < 2,5 s, CLS = 0, INP < 200 ms
Conclusion
Une architecture headless PrestaShop + Next.js bien construite offre un contrôle SEO total que le front natif PrestaShop ne peut pas atteindre. La clé est de toujours rendre côté serveur les données critiques pour l'indexation, de gérer précisément les canonical et les métadonnées, et de ne jamais exposer la logique de fetch côté client.
Vous construisez un front headless ou migrez une boutique existante ? Contactez-moi pour qu'on en discute.