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 (1–5) 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).