I have a headless nextjs app with strapi on next 14 with the app router. I’m using output:export to statically export my app on ngnix server. I had some problems to generate the static files for the dynamic routes. I solved it by using a client component to fetch the product data, so I separated the logic into 2 files :
page.tsx :
import { getStrapiData } from "app/api/api"
import ProductPage from "."
// Generate static parameters
export const revalidate = 60
export async function generateStaticParams() {
try {
// Fetch product data from the API
const { data: products } = await getStrapiData("/api/products")
// Map product IDs to static paths
return products.map((product) => ({
slug: product.slug,
}))
} catch (error) {
console.error("Error fetching product IDs:", error)
return [] // Return an empty array in case of an error
}
}
// Multiple versions of this page will be statically generated
// using the `params` returned by `generateStaticParams`
export default function ServerPage({ params }: { params: { slug: string } }) {
return <ProductPage params={{ slug: params.slug }} />
}
index.tsx :
"use client"
import { getProductBySlug } from "app/api/api"
import { useEffect, useState } from "react"
import { ProductData } from "types/types"
import Product from "@/components/Product/product"
import ProductDetails from "@/components/Product/productDetails"
import { pathBottom, pathTop } from "@/components/SVG/Stroke"
interface Props {
params: {
slug: string
}
}
export default function ProductPage({ params }: Props) {
const { slug } = params
console.log("Generating page for product slug:", slug)
const [product, setProduct] = useState<ProductData | null>(null)
useEffect(() => {
async function fetchData() {
try {
const response = await getProductBySlug(slug)
const productData = response.data[0] // Accès au premier élément du tableau data
console.log("Product fetched:", productData)
setProduct(productData)
} catch (error) {
console.error("Error fetching product data:", error)
}
}
fetchData()
}, [slug])
if (!product) {
return <div className="mx-auto mt-32 w-screen text-center text-xl text-blue">Chargement...</div> // Handle loading state if needed
}
const { game_bonux } = product
const color = game_bonux?.color
if (!color) {
console.error("Le produit ou sa propriété color est indéfini.")
return <div>Error: Product data incomplete.</div> // Handle error state
}
const svgTop = (
<svg
width="100%"
height="28"
viewBox="0 0 720 28"
preserveAspectRatio="none"
fill={color}
xmlns="http://www.w3.org/2000/svg"
>
{pathTop}
fill={color}
</svg>
)
const svgBottom = (
<svg
width="100%"
height="28"
viewBox="0 0 720 28"
preserveAspectRatio="none"
fill={color}
xmlns="http://www.w3.org/2000/svg"
>
{pathBottom}
fill={color}
</svg>
)
return (
<>
{/* Section Hero */}
<div className="mt-16 hidden md:block">{svgTop}</div>
<Product product={product} color={color} />
<div className="-mt-1 hidden md:block">{svgBottom}</div>
<ProductDetails product={product} />
</>
)
}
It worked but when I add a product in the back office, the params don’t seem to be revalidated, or at least the new routes is leading to a 404 error