Skip to content

Integration: Next.js

Status: Produktiv seit 2026-05-20. Funktioniert mit Next.js 14+ (App Router) und dem Pages Router. Das CLI erkennt automatisch, welcher Router in deinem Projekt verwendet wird.

60-Sekunden-Setup

bash
# In deinem Next.js-Projekt:
npx @peacock/cli init next

Was passiert:

  1. Erkennung: Sucht nach next.config.{js,mjs,ts,cjs} oder next in package.json.
  2. .env.local: Fügt drei Variablen idempotent hinzu (vorhandene Werte werden nicht überschrieben).
  3. Sample-Route: Erzeugt app/[...slug]/page.tsx (App Router) oder pages/[...slug].tsx (Pages Router) — abhängig davon, welcher Ordner bereits existiert. Vorhandene Datei wird nicht überschrieben.
  4. Druckt den Install-Befehl für dein pnpm/yarn/npm (erkannt am Lockfile).

Danach manuell:

bash
pnpm add @peacock/sdk-js          # oder yarn / npm
# .env.local mit echten Werten füllen
pnpm dev

Konfiguration

.env.local (Next-Konvention — wird nur lokal geladen, nicht in Git):

dotenv
NEXT_PUBLIC_PEACOCK_API_BASE_URL=https://your-peacock-host.example/v1
NEXT_PUBLIC_PEACOCK_SPACE=your-space-slug
PEACOCK_TOKEN=your-public-read-token

Warum NEXT_PUBLIC_-Präfix für API_BASE_URL und SPACE: Diese Werte sind nicht sensibel und werden bei statischem Pre-Rendering / Static Export ins Client-Bundle inliniert. Der Token bleibt ohne NEXT_PUBLIC_-Präfix, damit er nur serverseitig (Server Components, getServerSideProps, Route Handlers) verfügbar ist.

App Router (Next 14+)

Die generierte app/[...slug]/page.tsx:

tsx
import { makeApi } from '@peacock/sdk-js';
import { notFound } from 'next/navigation';

interface PageProps {
  params: Promise<{ slug?: string[] }>;
}

export default async function Page({ params }: PageProps) {
  const { slug } = await params;
  const path = '/' + (slug?.join('/') ?? '');

  const api = makeApi(process.env['PEACOCK_TOKEN'] ?? '', {
    baseUrl: process.env['NEXT_PUBLIC_PEACOCK_API_BASE_URL'] ?? '',
  });
  const result = await api.cdnStory(
    process.env['NEXT_PUBLIC_PEACOCK_SPACE'] ?? '',
    path,
  );

  if (result.status !== 'ok') {
    notFound();
  }

  return (
    <main>
      <pre>{JSON.stringify(result.data.data.content, null, 2)}</pre>
    </main>
  );
}

Das ist eine Server Component — kein Client-JS notwendig für den Read-Pfad. Die HTML-Antwort enthält bereits gerenderten Content auf den ersten Pageload.

Pages Router

Falls dein Projekt pages/-Routing verwendet, generiert das CLI stattdessen:

tsx
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next';
import { makeApi } from '@peacock/sdk-js';

export const getServerSideProps: GetServerSideProps = async (context) => {
  const slug = (context.params?.['slug'] as string[] | undefined) ?? [];
  const path = '/' + slug.join('/');

  const api = makeApi(process.env['PEACOCK_TOKEN'] ?? '', {
    baseUrl: process.env['NEXT_PUBLIC_PEACOCK_API_BASE_URL'] ?? '',
  });
  const result = await api.cdnStory(
    process.env['NEXT_PUBLIC_PEACOCK_SPACE'] ?? '',
    path,
  );

  if (result.status !== 'ok') {
    return { notFound: true };
  }

  return { props: { content: result.data.data.content } };
};

export default function StoryPage(
  props: InferGetServerSidePropsType<typeof getServerSideProps>,
) {
  return <main><pre>{JSON.stringify(props.content, null, 2)}</pre></main>;
}

Für statische Generierung statt SSR: Tausche getServerSideProps gegen getStaticProps + getStaticPaths.

Eigene Block-Komponenten

Das Sample rendert den rohen JSON-Tree. In echten Apps mapperst du jeden _component auf eine React-Komponente:

tsx
function renderBlock(block: { _component: string } & Record<string, unknown>) {
  switch (block._component) {
    case 'hero':       return <Hero {...block} />;
    case 'text_image': return <TextImage {...block} />;
    case 'faq':        return <FAQ {...block} />;
    default:           return null;
  }
}

Caching

Der cdnStory()-Aufruf trifft den CDN-Endpoint, der ETag + Cache-Control: public, max-age=60, stale-while-revalidate=300 setzt. In Next 14+ macht das App-Router-fetch-Wrapping diese Header automatisch wirksam — du bekommst gratis Edge-Caching, wenn du auf Vercel oder Cloudflare Pages deployst.

Für ISR (incremental static regeneration) im Pages Router setze revalidate in getStaticProps.

Was noch fehlt

  • Dedicated @peacock/sdk-next: Phase-9-Polish. Heute nutzt die Integration den framework-agnostischen @peacock/sdk-js — funktioniert vollständig, ist nur etwas verboser.
  • Image Optimization: Sobald @peacock/sdk-next landet, integriert es sich mit Next/Image für automatische loader-Konfiguration auf den Peacock-Assets-Endpoint.