Studio Docs

Framework Recipe: Next.js Pages Router

getServerSideProps fetches. _document.tsx injects styles + metadata. Page component renders.

Verified in-repo: smoke-tested end-to-end against the Studio Documentation Contentstack project (Next.js 14.2.35). curl /blog/welcome-to-studio returns 200 OK, 23 KB composition body, <style data-studio-ssr> with 360 --token-* properties, populated <title>, zero data-cs-defer-builtin placeholders.

Requires next build && next start (not next dev: Pages Router’s dev mode has racy module init).

Read Framework recipes: SSR integration first.

Do It With a Skill

configure-csr-vs-ssr picks and wires the render path for this framework. Run it before following the recipe by hand, or use the recipe to check what it wrote.

curl -fsSL https://studio-documentation.contentstackapps.com/install.sh | sh

The Recipe

// pages/[[...slug]].tsx
import { GetServerSideProps } from "next";
import { StudioComponent, getSSRStyleTags, getCompositionMetadata, } from "@contentstack/studio-react";
import { sdk } from "@/lib/studio.server";
import Head from "next/head";
import "@/registry";                     // Rule 2

export const getServerSideProps: GetServerSideProps = async ({ req, resolvedUrl }) => {
  const url = new URL(resolvedUrl, `http://${req.headers.host}`);
  const specOptions = await sdk.fetchCompositionData(
    { url: url.pathname, searchQuery: url.searchParams },
    { locale: "en-us" },
  );
  if (!specOptions?.spec) return { notFound: true };

  const styleTags = getSSRStyleTags(specOptions.spec);
  const metadata = getCompositionMetadata(specOptions, { baseUrl: `http://${req.headers.host}` });

  return { props: { specOptions, styleTags, metadata } };
};

export default function Page({ specOptions, styleTags, metadata }: any) {
  return (
    <>
      <Head>
        <title>{metadata.title}</title>
        <meta name="description" content={metadata.description} />
        {metadata.ogImage && <meta property="og:image" content={metadata.ogImage} />}
        {metadata.canonical && <link rel="canonical" href={metadata.canonical} />}
      </Head>
      <div dangerouslySetInnerHTML={{ __html: styleTags }} />
      <StudioComponent specOptions={specOptions} />
    </>
  );
}

Not-found handling is not shown here. This recipe is the CI-tested happy path. fetchCompositionData throws when no composition matches, so as written an unclaimed URL returns a 500, not a 404. Wrap it before shipping: see Rule 5 and the canonical resolveComposition helper.

_app.tsx (registry imported at app boot):

// pages/_app.tsx
import "@/registry";
import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}

Verify

Verification curl test. For Pages Router, run next build && next start, then curl.

Smoke Pass: Verify Before Treating This Recipe as Production-Ready

This recipe is desk-checked against the CI-tested Node and RSC recipes. It has not yet been end-to-end verified against a real Pages Router app. Before shipping in production, run:

  • Scaffold a fresh Next.js Pages Router project (create-next-app --pages-router).
  • Install @contentstack/studio-react (matching the version pinned in the SDK’s examples/nextjs-rsc/package.json).
  • Copy pages/[[...slug]].tsx + pages/_app.tsx + registry.ts from this recipe.
  • Wire a known-good Studio project with at least one saved composition matching a known URL.
  • Run next build && next start, NOT next dev. Dev-mode’s per-request recompile is racy for module init order.
  • Run the Verification curl test against a known composition URL. All three assertions must pass.
  • Verify with the browser Live Preview: edit the entry in Contentstack and confirm the browser reflects the change after router.replace(router.asPath).

Report deltas from the recipe as GitHub issues so the doc converges on truth.

Common Failures

See Troubleshooting. Pages-Router-specific quirks: verify SSR with next build && next start, not next dev. Dev mode’s per-request recompile is racy for module init order.