uidudevelopers
Recipes

Dynamic CMS routes

Generate a static page for every CMS slug at build time with generateStaticParams.

Legacy CMS

This recipe uses the legacy Projects CMS (listPages / getPage). For a Site, build the params from listSitePages — see step 6 of the Public website path.

A [slug]/page.tsx route that pre-renders every CMS page in your project.

The route

// app/[slug]/page.tsx
import { getPage, listPages } from '@uidu/client';
import { PageBlocks } from '@uidu/react';
import { notFound } from 'next/navigation';
import { client } from '@/lib/uidu';
import { Hero, Feature, ContactForm } from '@/components/blocks';

export async function generateStaticParams() {
  const pages = await listPages(client, {
    projectId: process.env.UIDU_PROJECT_ID!,
    first: 200,
  });
  return pages.map((p) => ({ slug: p.slug }));
}

export default async function CmsPage({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const page = await getPage(client, {
    projectId: process.env.UIDU_PROJECT_ID!,
    slug,
  });

  if (!page) notFound();

  return (
    <PageBlocks
      pageBlocks={page.pageBlocks}
      components={{
        Hero,
        feature1: Feature,
        ContactForm,
      }}
    />
  );
}

Why this works

  • listPages returns every page slug in the project — that's what generateStaticParams needs.
  • getPage is called once per slug at build time. Next.js caches the result per route.
  • <PageBlocks> looks up each block's templateBlock.shortname in your components map and renders the matching component with the block's fieldValues.

Add a sitemap

// app/sitemap.ts
import { listPages } from '@uidu/client';
import { client } from '@/lib/uidu';

export default async function sitemap() {
  const pages = await listPages(client, {
    projectId: process.env.UIDU_PROJECT_ID!,
    first: 500,
  });

  return pages.map((p) => ({
    url: `https://example.com/${p.slug}`,
    lastModified: p.updatedAt,
  }));
}

Add ISR revalidation

If you want the page to refresh without a redeploy, set a revalidation interval:

export const revalidate = 60; // seconds

For event-driven invalidation, configure a webhook in your uidu workspace to hit /api/revalidate?path=/<slug> and call revalidatePath from there.

For more than ~500 pages, walk every page with the paginate() helper.

On this page