Gutenberg ist der Standard-Editor für WordPress. Mit dem Editor kannst du Inhalte mit einzelnen Blöcken für Text, Bilder, Videos und andere Seitenelemente per Drag-and-Drop erstellen und gestalten. Dieser Ansatz verbessert die Flexibilität und die Gestaltungsmöglichkeiten von WordPress.

In diesem Leitfaden wird erklärt, wie du Gutenberg-Inhalte über die WordPress REST API in einer statischen Next.js-Website als HTML parsen kannst.

Voraussetzungen

Um dieser Anleitung zu folgen, brauchst du:

Abrufen von Gutenberg-Inhalten über eine REST-API

Um programmatisch mit deiner WordPress-Website zu interagieren und in Gutenberg-Blöcken strukturierte Inhalte abzurufen, verwendest du die WordPress REST API oder das WPGraphQL-Plugin. Mit diesen Tools kannst du deine WordPress-Inhalte im JSON-Format abrufen.

Um den Zugriff auf JSON-Daten über die REST-API zu ermöglichen, musst du die Permalink-Einstellungen in WordPress anpassen (nicht auf „Plain“). Dies ermöglicht den API-Zugriff über eine strukturierte URL, wie folgt:

https://yoursite.com/wp-json/wp/v2

Indem du API-Anfragen an diese URL stellst, kannst du programmatisch verschiedene Informationen abrufen und Vorgänge auf deiner WordPress-Website durchführen. Du kannst zum Beispiel eine Liste von Beiträgen abrufen, indem du eine GET-Anfrage hierhin sendest:

https://yoursite.com/wp-json/wp/v2/posts

Du erhältst ein JSON-Objekt mit Informationen über die Beiträge auf deiner WordPress-Website, einschließlich Titel, Inhalt, Autorendetails und mehr.

Gutenberg-Blöcke als HTML parsen

Wenn du Beiträge von einer WordPress-Website abrufst, die den Gutenberg-Editor verwendet, können die gespeicherten Inhalte in der Datenbank eine Mischung aus HTML- und JSON-Metadaten enthalten, um verschiedene Blocktypen wie Zitate und Galerien zu beschreiben. Zum Beispiel:

<!-- wp:quote {"className":"inspirational-quote","style":{"typography":{"fontSize":"large"}}} -->
<blockquote class="wp-block-quote inspirational-quote has-large-font-size"><p>“The journey of a thousand miles begins with one step.”</p><cite>Lao Tzu</cite></blockquote>
<!-- /wp:quote -->

<!-- wp:gallery {"ids":[34,35],"columns":2,"linkTo":"none","sizeSlug":"medium","className":"custom-gallery"} -->
<ul class="wp-block-gallery columns-2 is-cropped custom-gallery"><li class="blocks-gallery-item"><figure><img src="http://example.com/wp-content/uploads/2021/09/image1-300x200.jpg" alt="A breathtaking view of the mountains" class="wp-image-34"/></figure></li><li class="blocks-gallery-item"><figure><img src="http://example.com/wp-content/uploads/2021/09/image2-300x200.jpg" alt="Serene lakeside at dawn" class="wp-image-35"/></figure></li></ul>
<!-- /wp:gallery -->

Dieses Snippet zeigt zwei Gutenberg-Blöcke: ein Zitat und eine Galerie. Jeder ist mit JSON-Metadaten versehen, die in HTML-Kommentaren gekapselt sind. Die Metadaten definieren Attribute wie Klassennamen, Stile und andere Konfigurationen, die für die Darstellung des Blocks relevant sind.

Wenn du diese Blöcke über die WordPress REST API oder WPGraphQL abrufst, verarbeitet WordPress sie und wandelt die Kombination aus HTML- und JSON-Metadaten in vollständig gerenderte HTML-Elemente um, die du direkt in Webseiten einbinden kannst. Das umgewandelte HTML für die oben genannten Blöcke würde wie folgt aussehen:

<blockquote class="wp-block-quote inspirational-quote has-large-font-size"><p>“The journey of a thousand miles begins with one step.”</p><cite>Lao Tzu</cite></blockquote>

<ul class="wp-block-gallery columns-2 is-cropped custom-gallery">
  <li class="blocks-gallery-item"><figure><img loading="lazy" src="http://example.com/wp-content/uploads/2021/09/image1-300x200.jpg" alt="A breathtaking view of the mountains" class="wp-image-34" sizes="(max-width: 300px) 100vw, 300px" /></figure></li>
  <li class="blocks-gallery-item"><figure><img loading="lazy" src="http://example.com/wp-content/uploads/2021/09/image2-300x200.jpg" alt="Serene lakeside at dawn" class="wp-image-35" sizes="(max-width: 300px) 100vw, 300px" /></figure></li>
</ul>

Für Entwickler, die entkoppelte oder Headless-Anwendungen mit JavaScript-Frameworks wie Next.js erstellen, ist dies eine einfache Methode, um Inhalte anzuzeigen, indem sie das HTML direkt in die Seite einfügen und die dangerouslySetInnerHTML Eigenschaft zum Rendern des Markups verwenden.

<div dangerouslySetInnerHTML={{ __html: <raw_html_string> }} />

Zusätzlich musst du eventuell weitere Formatierungen für Elemente wie Links vornehmen und überflüssige Zeilenumbrüche behandeln (\n), was in diesem Leitfaden später erklärt wird.

Inhalt der Gutenberg-Blöcke in die statische Next.js-Site einbinden

In diesem Abschnitt werden wir WordPress-Inhalte in ein Next.js-Projekt holen und die Gutenberg-Blöcke als HTML parsen.

  1. Beginne damit, eine Funktion einzurichten, die Beiträge von deiner WordPress-Website abruft. Öffne die Datei src/page.js in deinem Projekt und ersetze ihren Inhalt durch den folgenden Codeschnipsel:
    const getWpPosts = async () => {
    	const res = await fetch('https://yoursite.com/wp-json/wp/v2/posts');
      	const posts = await res.json();
    	return posts;
    };

    Diese asynchrone Funktion führt eine API-Anfrage an die WordPress REST API durch. Sie holt alle auf deiner Seite verfügbaren Beiträge ab und gibt sie als Array zurück.

  2. Als Nächstes verwenden wir die abgerufenen Beiträge in einer einfachen Next.js-Seitenkomponente, indem wir die Beiträge auf der Konsole protokollieren und eine einfache Begrüßung darstellen:
    const page = async () => {
      const posts = await getWpPosts();
      console.log(posts);
      
      return (
        <div>
          <h1>Hello World</h1>
        </div>
      );
    };
    
    export default page;

    Wenn du dein Projekt mit npm run dev ausführst, zeigt es die Nachricht „Hello World“ an und protokolliert die abgerufenen Beiträge im Terminal.

    [
      {
        "_links" : {
          "about" : [...],
          "author" : [...],
          "collection" : [...],
          "curies" : [...],
          "predecessor-version" : [...],
          "replies" : [...],
          "self" : [...],
          "version-history" : [...],
          "wp:attachment" : [...],
          "wp:term" : [...]
        },
        "author" : 1,
        "categories" : [...],
        "comment_status" : "open",
        "content" : {
          "protected" : false,
          "rendered" : "\n<p>Fire, a primal force, captivates with its <strong>flickering flames</strong>, evoking both awe and caution. Its <quote>dance</quote> symbolizes destruction and renewal, consuming the old to make way for the new. While it warms our homes and hearts, fire demands respect for its power to devastate.</p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"250\" height=\"148\" src=\"https://img.example.com/wp-content/uploads/2024/02/burningbuilding.jpg\" alt=\"\" class=\"wp-image-14\"/></figure>\n\n\n\n<p>In ancient times, fire was a beacon of light and warmth, essential for survival. Today, it remains a symbol of human ingenuity and danger. From the comforting glow of a hearth to the destructive fury of wildfires, fire’s dual nature reminds us of our fragile relationship with the elements.</p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https://img.example.com/premium-photo/painting-burning-building-illuminated-by-bright-flames-night_168058-249.jpg?w=1380\" alt=\"\"/></figure>\n\n\n\n<p>You can check out other articles on our blog:</p>\n\n\n\n<ul>\n<li><a href=\"https://yoursite.com/?p=6\">Lorem Ipsum: Beginnings</a></li>\n\n\n\n<li><a href=\"https://yoursite.com/?p=9\">Lorem Ipsum: Act 2</a></li>\n\n\n\n<li><a href=\"https://yoursite.com/?p=11\">Lorem Ipsum: Act 3</a></li>\n</ul>\n"
        },
        "date" : "2024-02-27T12:08:30",
        "date_gmt" : "2024-02-27T12:08:30",
        "excerpt" : {
          "protected" : false,
          "rendered" : "<p>Fire, a primal force, captivates with its flickering flames, evoking both awe and caution. Its dance symbolizes destruction and renewal, consuming the old to make way for the new. While it warms our homes and hearts, fire demands respect for its power to devastate. In ancient times, fire was a beacon of light and warmth, […]</p>\n"
        },
        "featured_media" : 0,
        "format" : "standard",
        "guid" : {
          "rendered" : "https://yoursite.com/?p=13"
        },
        "id" : 13,
        "link" : "https://yoursite.com/?p=13",
        "meta" : {
          "footnotes" : ""
        },
        "modified" : "2024-02-29T16:45:36",
        "modified_gmt" : "2024-02-29T16:45:36",
        "ping_status" : "open",
        "slug" : "fire-fire",
        "status" : "publish",
        "sticky" : false,
        "tags" : [],
        "template" : "",
        "title" : {
          "rendered" : "Fire"
        },
        "type" : "post"
       },
      },
      ...
    ]

    Die JSON-Objekte, die die einzelnen Gutenberg-Beitragsdaten repräsentieren, enthalten verschiedene Felder, darunter die Felder content und excerpt, die als Gutenberg-Blöcke zurückgegeben und als HTML-Strings geparst werden.

  3. Um diese HTML-Inhalte in Next.js korrekt darzustellen, verwenden wir die Eigenschaft dangerouslySetInnerHTML:
    const page = async () => {
      const posts = await getWpPosts();
    
      return (
        <>
          <h1> Headless Blog </h1>
    
          <div>
            {posts.map((post) => (
              <Link href={'/blog/' + post.id} key={post.id}>
                <h2>
                  {post.title.rendered} <span>-></span>
                </h2>
                <div dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }} />
              </Link>
            ))}
          </div>
        </>
      );
    };
    
    export default page;

    In dieser aktualisierten Komponente mappen wir das Array der abgerufenen Beiträge, um eine Liste von Beitragsauszügen zu erstellen. Jeder Auszug wird für die Navigation in eine Link Komponente verpackt, die den Titel des Beitrags und einen Ausschnitt des Inhalts anzeigt.

    Die Eigenschaft dangerouslySetInnerHTML wird verwendet, um den HTML-Inhalt des Feldes excerpt.rendered zu parsen und zu rendern.

  4. Als nächstes erstellst du eine Datei blog/[id]/page.js im Anwendungs-Verzeichnis. Du verwendest Ordner, um Routen zu definieren. Indem du also einen Blog-Ordner erstellst, definierst du die Blog-Route. Du kombinierst dies mit dynamischem Routing, um Routen für jeden Beitrag zu erstellen.
  5. Jeder Beitrag hat eine ID. Mit dieser ID erstellst du eine eindeutige Route, /blog/{post_id}, in deiner Anwendung. Füge den folgenden Code ein:
    import Link from 'next/link';
    
    export async function generateStaticParams() {
        const res = await fetch('https://yoursite.com/wp-json/wp/v2/posts');
        const posts = await res.json();
        return posts.map((post) => {
            return {
                params: {
                    id: post.id.toString(),
                },
            };
        });
    }
    
    export async function getPost(id) {
        const response = await fetch('https://yoursite.com/wp-json/wp/v2/posts/' + id);
        const post = await response.json();
        return post;
    }

    Die Funktion generateStaticParams() generiert statisch Routen zur Erstellungszeit auf der Grundlage der entsprechenden ID, die für jeden Beitrag zurückgegeben wird. Die Funktion getPost() holt Gutenberg-Daten von der REST-API für den Beitrag mit einer übergebenen ID.

    Ein früherer Abschnitt zeigte ein Beispiel für geparste Gutenberg-Daten, die von der REST-API für einen Beitrag zurückgegeben wurden. Im Moment interessieren wir uns nur für das Feld content.rendered:

    [
      {
        ...
        "content": {
          "rendered" : "\n<p>Fire, a primal force, captivates with its <strong>flickering flames</strong>, evoking both awe and caution. Its <quote>dance</quote> symbolizes destruction and renewal, consuming the old to make way for the new. While it warms our homes and hearts, fire demands respect for its power to devastate.</p>\n\n\n\n<figure> class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"250\" height=\"148\" src=\"https://img.example.com/wp-content/uploads/2024/02/burningbuilding.jpg\" alt=\"\" class=\"wp-image-14\"/></figure>\n\n\n\n<p>In ancient times, fire was a beacon of light and warmth, essential for survival. Today, it remains a symbol of human ingenuity and danger. From the comforting glow of a hearth to the destructive fury of wildfires, fire’s dual nature reminds us of our fragile relationship with the elements.</p>\n\n\n\n<figure> class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https://img.example.com/premium-photo/painting-burning-building-illuminated-by-bright-flames-night_168058-249.jpg?w=1380\" alt=\"\"/></figure>\n\n\n\n<p>You can check out other articles on our blog:</p>\n\n\n\n<ul>\n<li><a> href=\"https://yoursite.com/?p=6\">Lorem Ipsum: Beginnings</a></li>\n\n\n\n<li><a> href=\"https://yoursite.com/?p=9\">Lorem Ipsum: Act 2</a></li>\n\n\n\n<li><a> href=\"https://yoursite.com/?p=11\">Lorem Ipsum: Act 3</a></li>\n</ul>\n"
        },
        ...
      }
    ]

    Dieses Feld enthält das Roh-HTML des Beitrags. Es kann direkt mit der Eigenschaft dangerouslySetInnerHTML wie folgt gerendert werden: <div dangerouslySetInnerHTML={{ __html: <raw_html_string> }} />.

  6. Als Nächstes kannst du die Daten verarbeiten, indem du interne Links analysierst und die Größe von Bildern änderst. Installiere das Paket html-react-parser, um das Parsen von Tags zu vereinfachen:
    npm install html-react-parser --save
  7. Füge den folgenden Code in die Datei blog/[id]/page.js ein:
    import parse, { domToReact } from "html-react-parser";
    
    /*
     * We use a regular expression (pattern) to match the specific URL you want to replace.
     * The (\d+) part captures the numeric ID after ?p=.
     * Then, we use the replacement string 'data-internal-link="true" href="/blog/$1"',
     * where $1 is a placeholder for the captured ID.
     */
    export function fixInternalLinks(html_string) {
      const pattern = /href="https:\/\/yoursite.com\/\?p=(\d+)"/g;
      const replacement = 'data-internal-link="true" href="/blog/$1"';
    
      return html_string.replace(pattern, replacement);
    }
    
    export function parseHtml(html) {
      // Replace 2+ sequences of '\n' with a single '<br />' tag
      const _content = html.replace(/\n{2,}/g, '<br />');
      const content = fixInternalLinks(_content);
    
      const options = {
        replace: ({ name, attribs, children }) => {
          // Convert internal links to Next.js Link components.
          const isInternalLink =
            name === "a" && attribs["data-internal-link"] === "true";
    
          if (isInternalLink) {
            return (
              <Link href={attribs.href} {...attribs}>
                {domToReact(children, options)}
              </Link>
        	  );
          } else if (name === "img") {
            attribs["width"] = "250";
            attribs["height"] = "150";
            return (
              <img {...attribs}/>
            );
          }
        },
      };
    
      return parse(content, options);
    }

    Die Funktion fixInternalLinks() verwendet einen regulären Ausdruck, um im HTML-String Links zu Beiträgen auf deiner WordPress-Seite zu finden. Im rohen HTML kannst du sehen, dass der Beitrag ein List Tag mit Links zu anderen Beiträgen auf deiner Seite enthält. Diese Links werden durch interne Links zu Routen auf deiner statischen Seite ersetzt.

    Die Funktion parseHTML() findet mehrere Sequenzen von überflüssigen Zeilenumbrüchen, nund ersetzt sie durch <br /> Tags. Sie findet auch interne Links und wandelt die Anker-Tags in Link-Tags um. Anschließend passt diese Funktion die Größe von Bildern mithilfe von Tag-Attributen an.

  8. Um die Haupt-UI für jede dynamische Route zu erstellen, füge den folgenden Code ein:
    export default async function Post({ params }) {
      const post = await getPost(params.id);
    
      const content = parseHtml(post.content.rendered);
    
      return (
        <>
          <h1>
            {post.title.rendered}
          </h1>
     	 
          <div>{content}</div>
        </>
      );
    }

    Nach dem Parsen des Roh-HTML aus den Gutenberg-Daten gibt der Code JSX zurück, das die formatierte Benutzeroberfläche der Seite darstellt.

Wenn du dein Projekt ausführst, wird auf der Startseite eine Liste der Beiträge in deinem WordPress angezeigt. Wenn du auf einzelne Beiträge klickst, werden die geparsten Gutenberg-Inhalte richtig dargestellt.

Bereitstellen deiner statischen Next.js-Website auf Kinsta

Wenn du Headless WordPress mit modernen Frameworks wie Next.js kombinierst, ist es wichtig, eine kosteneffiziente Lösung für die Bereitstellung zu finden, vor allem wenn du ein leistungsstarkes WordPress-Hosting wie das von Kinsta für deine WordPress-Website nutzt. Der Statische-Seiten-Hosting Dienst von Kinsta bietet eine nahtlose und kostengünstige Möglichkeit, deine Website online zu stellen.

Kinsta ermöglicht es dir, bis zu 100 statische Websites kostenlos zu hosten. Als erstes musst du deinen Code zu einem bevorzugten Git-Anbieter (Bitbucket, GitHub oder GitLab) pushen. Sobald dein Repo fertig ist, befolge diese Schritte, um deine statische Website bei Kinsta einzurichten:

  1. Logge dich ein oder erstelle ein Konto, um dein MyKinsta-Dashboard zu sehen.
  2. Autorisiere Kinsta bei deinem Git-Anbieter.
  3. Klicke in der linken Seitenleiste auf Statische Seiten und dann auf Website hinzufügen.
  4. Wähle das Repository und den Branch aus, von dem aus du bereitstellen möchtest.
  5. Gib deiner Site einen eindeutigen Namen.
  6. Füge die Build-Einstellungen in folgendem Format hinzu:
    • Build-Befehl: npm run build
    • Node-Version: 18.16.0
    • Verzeichnis veröffentlichen: out
  7. Zum Schluss klickst du auf Website erstellen.

Und das war’s! Innerhalb weniger Sekunden hast du eine fertige Website. Du erhältst einen Link, über den du auf die bereitgestellte Version deiner Website zugreifen kannst. Du kannst später deine eigene Domain und dein SSL-Zertifikat hinzufügen, wenn du möchtest.

Als Alternative zum statischen Website-Hosting kannst du deine statische Website auch mit dem Anwendungs-Hosting Dienst von Kinsta bereitstellen. Dieser bietet eine größere Hosting-Flexibilität, ein breiteres Spektrum an Vorteilen und Zugang zu robusteren Funktionen wie Skalierbarkeit, individuelle Bereitstellung mit einem Dockerfile und umfassende Analysen mit Echtzeit- und historischen Daten. Außerdem musst du dein Next.js-Projekt nicht für statisches Rendering konfigurieren.

Zusammenfassung

In diesem Leitfaden wurde erklärt, wie du Gutenberg-Blockinhalte über die WordPress-API effektiv als HTML integrieren und parsen kannst. Das macht es möglich, jede Art von Inhalt auf deinem Frontend zu rendern, wenn du Headless WordPress verwendest.

Du kannst dein Headless WordPress auf unserem Managed WordPress Hosting Service hosten und deine statische Website auf unserem Statische-Seiten-Hosting Dienst bereitstellen. Das bedeutet, dass du alles über deine Website in einem einzigen Dashboard findest: MyKinsta.

Wenn du dich für Kinsta entscheidest, profitierst du von einem Hosting-Anbieter, der die optimale Leistung und Skalierbarkeit deiner Website in den Vordergrund stellt und sie gleichzeitig mit fortschrittlichen Sicherheitsmaßnahmen schützt. Teste Kinsta noch heute!

Was hältst du von Headless WordPress und seiner Darstellung? Hast du eine bessere Möglichkeit, Gutenberg-Blöcke zu integrieren? Teile deine Ideen im Kommentarbereich mit!

Jeremy Holcombe Kinsta

Content & Marketing Editor bei Kinsta, WordPress Web Developer und Content Writer. Außerhalb von WordPress genieße ich den Strand, Golf und Filme. Außerdem habe ich Probleme mit großen Menschen ;).