Skip to content

Peacock in eine bestehende Seite einbauen

Kein Rewrite nötig. Peacock geht in jede Webseite — egal ob Astro, Next, Nuxt, Laravel, WordPress, SvelteKit, Remix, Eleventy, oder pure HTML. Drei Modi, je nachdem wie tief du integrieren willst.

In 30 Sekunden ausprobieren (ohne eigenes Setup)

Wenn du nur sehen willst, dass die CDN-API live ist und der Embed-Script wirklich funktioniert — die Public-Demo-Space ist eingerichtet und 24/7 erreichbar:

1) cURL gegen die Demo-Story:

bash
curl https://peacock-cms.webhoch.com/v1/cdn/demo/stories/welcome

Du bekommst {data: {uuid, slug, full_path, lang, content, meta}} zurück. Das ist die genau gleiche Antwort, die der Embed-Script in jede Seite rendert.

2) Embed in eine leere HTML-Datei (auf jedem Rechner mit Browser):

html
<!doctype html>
<html lang="de">
<body>
  <div data-peacock-story="/welcome"></div>
  <script src="https://peacock-cms.webhoch.com/embed.js"
          data-peacock-api="https://peacock-cms.webhoch.com"
          data-peacock-space="demo"
          defer></script>
</body>
</html>

Datei lokal als test.html speichern, doppelklicken → die Welcome-Story hydratisiert in den <div>. Wenn das funktioniert, weißt du, dass dein Browser, dein Netzwerk und der Peacock-Stack alle drei OK sind. Dann unten weiterlesen, wenn du dieselbe Mechanik mit deiner eigenen Space-Slug in deine echte Seite einbauen willst.


TL;DR

A) Embed-Script    — 1 Script-Tag + 1 div. Pure HTML, jQuery, alte Seiten. Zero Build.
B) Framework-CLI   — `npx @peacock/cli init <framework>` schreibt euch ein
                     Beispiel-Setup. Astro/Next/Nuxt/Laravel/WordPress/SvelteKit/Remix/Eleventy.
C) Manual SDK      — In bestehendem Code: `import { createClient } from '@peacock/sdk-js'`,
                     `await peacock.getStory('/about')`. Volle Kontrolle.

Jeder Mode redet mit derselben CDN-API — du kannst mit A anfangen und später auf C migrieren, ohne Content neu zu pflegen.


Mode A — Embed-Script (für jede HTML-Seite)

Der einfachste Weg. Funktioniert in WordPress-Themes ohne Code-Zugriff, in alten jQuery-Seiten, in Webflow-Custom-HTML-Blöcken, überall.

Schritt 1 — Script einbinden (Head oder Body, egal)

Empfohlen für Production: versionierte URL + SRI-Hash (= „Supply-Chain- Schutz, falls peacock-cms.webhoch.com kompromittiert wird"):

html
<script
  src="https://peacock-cms.webhoch.com/embed-v0.1.0.js"
  integrity="sha384-R2FMQxAk59Fbs+ln2bREi/U26a3VrRPa9AIo4w0aop9b/mHPJlmZToP9UrJg17RM"
  crossorigin="anonymous"
  data-peacock-api="https://peacock-cms.webhoch.com"
  data-peacock-space="dein-space-slug"
  defer
></script>

Mit integrity weigert sich der Browser, ein nicht-signaturgleiches Script auszuführen — selbst wenn jemand peacock-cms.webhoch.com übernimmt. Update-Pfad: wenn eine neue Version live ist (embed-v0.1.1.js), bekommt sie eine neue SRI; du musst beide Werte einmal austauschen.

CORS für SRI ist live (seit 2026-05-27)

crossorigin="anonymous" aktiviert den SRI-Check, triggert aber gleichzeitig einen CORS-Preflight. Peacock liefert die Drop-in-Scripts (/embed.js, /embed-v*.js, /peacock-admin.js) mit Access-Control-Allow-Origin: *

  • Cross-Origin-Resource-Policy: cross-origin und beantwortet OPTIONS mit 204 — d.h. der SRI-Tag funktioniert direkt, ohne dass du irgendwas am Nginx deiner Seite anpassen musst.

Vor 2026-05-27 war das anders: derselbe Snippet hat in Chrome mit "CORS-Error" abgebrochen. Falls du den alten SRI-Setup hattest und auf einen Cache-bust wartest — ?v=2026-05-27 oder den embed-v0.1.0.js- Pfad direkt frisch laden.

Quick-Start ohne SRI (= „immer-die-neueste, akzeptiert implizites Vertrauen"):

html
<script
  src="https://peacock-cms.webhoch.com/embed.js"
  data-peacock-api="https://peacock-cms.webhoch.com"
  data-peacock-space="dein-space-slug"
  defer
></script>

Diese Variante updatet sich automatisch mit jedem neuen Peacock- Release. Nachteil: weniger Schutz gegen einen kompromittierten CDN-Origin. Vorteil: kein manueller SRI-Bump pro Update.

Schritt 2 — Story-Container platzieren

Überall in deinem HTML, an der Stelle wo der CMS-Content erscheinen soll:

html
<div data-peacock-story="/about"></div>

Der Embed-Script findet das Element, holt die Story von /v1/cdn/<space>/stories/about, und rendert sie ein. Loading-State + Error-Handling sind eingebaut.

Pro-Funktionen

html
<!-- Andere Sprache -->
<div data-peacock-story="/welcome" data-peacock-lang="en-GB"></div>

<!-- Audience-Segment forcen (Phase 10) -->
<div data-peacock-story="/hero" data-peacock-segment="mobile"></div>

<!-- Mehrere Stories auf einer Seite -->
<div data-peacock-story="/hero"></div>
<aside data-peacock-story="/sidebar-promo"></aside>
<footer data-peacock-story="/footer"></footer>

<!-- Heading-Offset: deine Seite hat schon eine <h1>, der Embed-Hero soll <h2> sein -->
<script src="https://peacock-cms.webhoch.com/embed.js"
        data-peacock-api="https://peacock-cms.webhoch.com"
        data-peacock-space="dein-space-slug"
        data-peacock-heading-offset="1"
        defer></script>

<!-- ...oder pro Container, falls eine Sektion anders gestaffelt sein soll -->
<div data-peacock-story="/sidebar"
     data-peacock-heading-offset="2"></div>

data-peacock-heading-offset (15) verschiebt jede emittierte Heading nach unten — 1 = <h1><h2>, 2 = <h1><h3>, usw. bis maximal <h6>. Behebt den Heading-Hierarchie-Konflikt, wenn deine Seite schon eine eigene <h1> hat (Pflicht für saubere Accessibility + SEO). Der Wert auf dem <script>-Tag setzt einen Site-weiten Default; einzelne [data-peacock-story]-Container können den Default überschreiben.

SPA / dynamic mount

Wenn deine Seite Story-Container dynamisch einsetzt (React/Vue/Svelte ohne Build-Integration), rufe nach dem Mount window.peacockEmbed.hydrate() auf. Das scannt das DOM noch einmal und hydriert neue Container.

Was der Script kann (out-of-the-box)

  • Renderer für die 9 Starter-Blueprints: hero, rich_text, text_block, text_image, gallery, faq, pricing, cta, quote
  • Eigene Blueprints zeigen einen Hinweis (keine stille Disappearance)
  • XSS-Schutz: alle Strings durch textContent; Rich-Text-HTML durch einen Allowlist-Walker (nur <p> <strong> <em> <a> <h2> <h3> <ul> <li> <code> …)
  • Loading-Skeleton bis die CDN-Response ankommt
  • 13 KB Vanilla-JS, keine Dependencies

Styling

Der Script setzt nur class="peacock-hero", class="peacock-gallery" etc. — kein eigenes CSS. Style sie wie alle anderen Elemente deiner Seite. Tipp: ein 50-Zeilen-peacock-defaults.css ist genug für ein brauchbares Default-Look.


Mode B — Framework-CLI

Eine Zeile fügt Peacock zu deinem bestehenden Projekt hinzu. Der Installer detected dein Framework automatisch.

bash
# In deinem Projekt-Root:
npx @peacock/cli init
# → erkennt astro/next/nuxt/laravel/wordpress/sveltekit/remix/eleventy
# → schreibt die richtigen Sample-Dateien
# → druckt den nächsten Schritt

Was geschrieben wird:

  • .env mit PEACOCK_API_BASE_URL, PEACOCK_SPACE, PEACOCK_TOKEN (Placeholder — du füllst sie aus)
  • Eine Beispiel-Route, die den richtigen SDK-Aufruf macht — z.B. bei Astro src/pages/[...slug].astro mit getStory(Astro.params.slug)
  • Pro Framework eine peacock.config.* mit deiner Konfiguration

Der Installer schreibt nicht in bestehende Dateien. Wenn die Beispiel-Route schon existiert, lässt er sie in Ruhe und sagt ausdrücklich, was du manuell mergen musst. Idempotent — re-runs sind sicher.


Mode C — Manual SDK

Du hast schon einen Build-Pipeline und willst keinen Generator? Drei Schritte:

bash
# In deinem Projekt
pnpm add @peacock/sdk-js     # 5 KB ESM, framework-agnostisch
ts
// Irgendwo in deinem Code (Server-side, build-time, oder client-side)
import { createClient } from '@peacock/sdk-js';

const peacock = createClient({
  baseUrl:   process.env.PEACOCK_API_BASE_URL!,   // https://peacock-cms.webhoch.com
  spaceSlug: process.env.PEACOCK_SPACE!,          // "dein-space-slug"
});

const result = await peacock.getStory('/about', { lang: 'de-AT' });
if (result.status !== 'ok') {
  // 'not-found' | 'space-archived' | 'error' | 'not-modified'
  return notFound();
}
const story = result.story;
// story.content ist das JSON deiner Story — render wie du willst.

Volle TypeScript-Types, discriminated-union returns (kein try/catch nötig). Geht auch im Worker, in Cloudflare-Pages, in Lambda, in Deno — alles was fetch kann.

Mit Audience-Segments (Phase 10)

ts
const result = await peacock.getStory('/welcome', {
  segment: 'newsletter-may',   // forciert ein bestimmtes Segment
  lang: 'de-AT',
});

Ohne explizites Segment macht die CDN automatisches Matching via Headers (Country/Language/Device/Referrer/UTM) — siehe Personalisation.


Welcher Modus für welches Projekt?

Du hast …Empfohlen
Eine Astro/Next/Nuxt-App in BauFramework-CLI
Ein WordPress mit Custom-Theme, Code-ZugriffFramework-CLI (WordPress-Plugin)
Ein WordPress, nur Block-EditorEmbed-Script in Custom-HTML-Block
Eine statische HTML-SiteEmbed-Script
Webflow mit Custom-CodeEmbed-Script — Webflow-Guide
Wix Business/CommerceEmbed-Script — Wix-Guide
Squarespace Business/CommerceEmbed-Script — Squarespace-Guide
Eigene Framework-/Build-PipelineManual SDK
Du willst nur EINEN Block ersetzenEmbed-Script
Multi-Page, Multi-Locale, Edge-CacheFramework-CLI (Astro/Next)

SEO & Crawler — wann welcher Modus

Suchmaschinen und KI-Crawler indexieren die drei Modi unterschiedlich gut, weil sie das HTML zum unterschiedlichen Zeitpunkt produzieren. Faustregel: Mode B (SSR/SSG) > Mode C (eigene Pipeline) > Mode A (Embed).

ModusWas der Crawler sieht beim ersten FetchEmpfohlen für SEO
A — Embed-ScriptDen leeren Container <div data-peacock-story="…"></div>, bis der Crawler JS evaluiert und das fetch() ausgeführt hat. Google, Bing und moderne KI-Crawler können das, aber langsamer und weniger zuverlässig als reines HTML.Interne Tools, Marketing-Banner, Blog-Snippets, A/B-Tests. Nicht für SEO-kritische Hauptseiten.
B — Framework-CLI mit SSR/SSGDen vollen HTML-Output mit Content schon im ersten Request. Astro prerender, Next app/-Server-Components, Nuxt useFetch mit server: true, Laravel-Blade.Ja, optimal für SEO. Erste Wahl für Startseite, Produkt-Landingpages, alles mit Conversion-Ziel.
C — Manual SDKHängt von deiner Pipeline ab. Bei Server-Side getStory() → wie Mode B. Bei reinem Client-Code → wie Mode A.Hängt von deiner Konfiguration ab. Empfehlung: SDK auf Server/Edge laufen lassen, im Browser nur den fertigen HTML-Stream.

Progressive Enhancement — von Mode A nach Mode B migrieren

Du startest mit Embed-Mode (1 Tag Aufwand), und sobald SEO ein Thema wird, migrierst du dieselbe Story auf SSR — ohne den Content neu zu pflegen:

php
{{-- Beispiel: Laravel-Blade, Mode C, SSR --}}
@php
  $story = \Peacock\Sdk::space(env('PEACOCK_SPACE'))
    ->getStory($request->path());
@endphp

@if ($story)
  {{-- Server-side rendered Crawler sieht das HTML direkt. --}}
  <x-peacock-blocks :content="$story->content" />
@else
  {{-- Fallback: leerer Container, den der Embed-Script hydriert
       wenn Server-Render aussetzt. Defense in depth. --}}
  <div data-peacock-story="{{ $request->path() }}"></div>
@endif

Crawler sehen den <x-peacock-blocks>-Output sofort; falls dein SSR-Aufruf abbricht (CDN down, Timeout), übernimmt der Embed-Script im Client. Beide reden mit derselben CDN — gleicher Content, zwei Render-Wege.

<noscript>-Fallback im Embed-Container

Wenn du Mode A nutzt und JS-deaktivierte Visitors oder Tracker-Blocker ein Default-Statement sehen sollen, pack einen <noscript>-Block in den Container:

html
<div data-peacock-story="/about">
  <noscript>
    Bitte aktiviere JavaScript, um diesen Inhalt zu sehen.
    Alternativ: <a href="/about-static.html">statische Variante</a>.
  </noscript>
</div>

Der Embed-Script ersetzt alle Kinder des Containers bei der Hydration — der <noscript>-Block ist nur sichtbar, wenn JS gar nicht läuft.


Authentifizierung

Embed-Script + Framework-CLI + Manual SDK auf /v1/cdn/...-Endpunkten: keine Auth. Public read.

Wichtig: /v1/cdn/* ist unbedingt öffentlich

Jede Story mit Status published in einem nicht-archivierten Space ist über /v1/cdn/{space-slug}/stories/{path} anonym lesbar — ohne Token, ohne Cookie, ohne Referer-Check. Es gibt kein public_read-Flag, das man umlegen könnte, um einzelne Spaces zu schützen.

Wenn du Inhalte hast, die nicht öffentlich sein dürfen:

  • Lass die Story unpubliziert (status != published) — dann liefert die CDN 404.
  • Oder archiviere den ganzen Space (archived_at IS NOT NULL).
  • Oder pflege sensiblen Content gar nicht erst in Peacock — unser Modell ist "headless CMS für veröffentliche Inhalte".

Wenn du Stories programmatisch erzeugen oder updaten willst (z.B. ein Import-Script): Management-Token. In der Admin-UI: Settings → API Tokens → New token. Pass ihn als Authorization: Bearer <token> an /v1/spaces/<slug>/...-Endpoints.


Live-Testen

Eine fertige Demo-Seite mit Embed-Script:

html
<!doctype html>
<html lang="de">
<head>
  <meta charset="utf-8" />
  <title>Embed-Demo</title>
  <style>
    body { font-family: system-ui; max-width: 720px; margin: 2em auto; padding: 0 1em; }
    .peacock-hero h1 { font-size: 2.5em; line-height: 1.1; }
    .peacock-cta { display: inline-block; padding: 0.5em 1em; background: #0f6473; color: white; text-decoration: none; }
  </style>
</head>
<body>
  <main>
    <div data-peacock-story="/welcome"></div>
  </main>

  <script
    src="https://peacock-cms.webhoch.com/embed.js"
    data-peacock-api="https://peacock-cms.webhoch.com"
    data-peacock-space="dein-space-slug"
    defer
  ></script>
</body>
</html>

Lokal als demo.html speichern, öffnen, der Container füllt sich. Lade-Zeit: ~200 ms bei Cache-Hit, ~600 ms bei Cache-Miss (Cloudflare Frontside-Caching macht's nach ein paar Requests instant).