Gutenberg è l’editor predefinito di WordPress. L’editor permette di creare e modellare i contenuti utilizzando blocchi discreti per testo, immagini, video e altri elementi del sito attraverso un’interfaccia drag-and-drop. Questo approccio migliora la flessibilità e le capacità di progettazione di WordPress.

Questa guida spiega come eseguire il parse dei contenuti di Gutenberg come HTML utilizzando l’API REST di WordPress in un sito statico Next.js.

Prerequisiti

Per seguirci, serviranno:

Recuperare i contenuti di Gutenberg utilizzando un’API REST

Per interagire con il sito WordPress in modo programmatico e recuperare i contenuti strutturati nei blocchi di Gutenberg, utilizziamo l’API REST di WordPress o il plugin WPGraphQL. Questi strumenti permettono di recuperare i contenuti di WordPress in formato JSON.

Per abilitare l’accesso ai dati JSON tramite l’API REST, regoliamo le impostazioni dei permalink di WordPress in modo che non siano “Semplici”. In questo modo potremo accedere all’API attraverso un URL strutturato come il seguente:

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

Effettuando richieste API a questo URL, possiamo recuperare programmaticamente varie informazioni ed eseguire operazioni sul nostro sito WordPress. Ad esempio, possiamo recuperare un elenco di post inviando una richiesta GET a:

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

Questo restituirà un oggetto JSON contenente informazioni sui post del nostro sito WordPress, tra cui titoli, contenuti, dettagli sull’autore e altro ancora.

Eseguire il parse dei blocchi di Gutenberg come HTML

Quando si recuperano i post da un sito WordPress che utilizza l’editor Gutenberg, il contenuto memorizzato nel database può presentare una miscela di metadati HTML e JSON per descrivere vari tipi di blocchi, come citazioni e gallerie. Ad esempio:

<!-- 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 -->

Questo frammento illustra due blocchi di Gutenberg: una citazione e una galleria. Ognuno di essi è arricchito da metadati JSON incapsulati all’interno di commenti HTML. I metadati definiscono attributi come nomi di classi, stili e altre configurazioni rilevanti per la presentazione del blocco.

Quando recuperiamo questi blocchi tramite l’API REST di WordPress o WPGraphQL, WordPress li elabora, trasformando la combinazione di metadati HTML e JSON in elementi HTML completamente renderizzati che possiamo incorporare direttamente nelle pagine web. L’HTML trasformato per i blocchi di cui sopra appare come segue:

<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>

Per gli sviluppatori che realizzano applicazioni disaccoppiate o headless utilizzando framework JavaScript come Next.js, questo rappresenta un metodo semplice per visualizzare i contenuti iniettando direttamente l’HTML nella pagina utilizzando la proprietà dangerouslySetInnerHTML per rendere il markup.

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

Inoltre, potrebbe essere necessario eseguire un’ulteriore formattazione per gli elementi come i link e gestire i caratteri newline in eccesso (\n), come spiegato in seguito in questa guida.

Eseguire il parse del contenuto dei blocchi di Gutenberg nel sito statico Next.js

In questa sezione, recuperiamo i contenuti di WordPress in un progetto Next.js e poi eseguiamo il parse dei blocchi di Gutenberg come HTML.

  1. Iniziamo impostando una funzione per recuperare i post dal sito WordPress. Apriamo il file src/page.js nel progetto e sostituiamo il suo contenuto con il seguente frammento di codice:
    const getWpPosts = async () => {
    	const res = await fetch('https://yoursite.com/wp-json/wp/v2/posts');
      	const posts = await res.json();
    	return posts;
    };

    Questa funzione asincrona esegue una richiesta API all’API REST di WordPress. Recupera tutti i post disponibili sul sito e li restituisce come array.

  2. Successivamente, utilizziamo i post recuperati all’interno di un semplice componente di pagina Next.js registrando i post nella console e renderizzando un saluto di base:
    const page = async () => {
      const posts = await getWpPosts();
      console.log(posts);
      
      return (
        <div>
          <h1>Hello World</h1>
        </div>
      );
    };
    
    export default page;

    Quando eseguiamo il progetto utilizzando npm run dev, viene visualizzato il messaggio “Hello World” e vengono registrati i post recuperati nel Terminale.

    [
      {
        "_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"
       },
      },
      ...
    ]

    Gli oggetti JSON che rappresentano i dati dei singoli post di Gutenberg includono vari campi, tra cui il contenuto e l’estratto che vengono restituiti come blocchi di Gutenberg analizzati come stringhe HTML.

  3. Per rendere correttamente il contenuto HTML in Next.js, utilizziamo la proprietà 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 questo componente aggiornato, mappiamo l’array di post recuperati per generare un elenco di estratti di post. Ogni estratto è racchiuso in un componente Link per la navigazione, che mostra il titolo del post e un frammento del suo contenuto.

    La proprietà dangerouslySetInnerHTML viene utilizzata per analizzare e rendere il contenuto HTML contenuto nel campo excerpt.rendered.

  4. Quindi, creiamo un file blog/[id]/page.js nella cartella app. Usiamo le cartelle per definire le route. Quindi, creando una cartella blog, definiamo la route blog. Combinando questo con il routing dinamico, possiamo generare route per ogni post.
  5. Ogni post ha un ID. Utilizzeremo questo ID per generare la sua unica route, /blog/{post_id} nell’applicazione. Aggiungiamo il seguente codice:
    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;
    }

    La funzione generateStaticParams() genera staticamente i percorsi al momento della creazione in base all’ID corrispondente restituito da ogni post. La funzione getPost() recupera i dati di Gutenberg dall’API REST per il post con l’ID passato.

    In una sezione precedente abbiamo mostrato un esempio di parsing dei dati di Gutenberg restituiti dall’API REST per un post. Per ora ci interessa solo il campo 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"
        },
        ...
      }
    ]

    Questo campo contiene l’HTML grezzo del post. Può essere reso direttamente utilizzando la proprietà dangerouslySetInnerHTML in questo modo, <div dangerouslySetInnerHTML={{ __html: <raw_html_string> }} />.

  6. Successivamente, potremo elaborare i dati analizzando i link interni e ridimensionando le immagini. Installiamo il pacchetto html-react-parser per semplificare il processo di analisi dei tag:
    npm install html-react-parser --save
  7. Aggiungiamo il seguente codice al file blog/[id]/page.js:
    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);
    }

    La funzione fixInternalLinks() utilizza un’espressione regolare per trovare i link ai post del sito WordPress dalla stringa HTML. Nell’HTML grezzo, possiamo vedere che il post contiene un tag List con link ad altri post del sito, sostituendo questi link con link interni a percorsi del sito statico.

    La funzione parseHTML() individua sequenze multiple di newline in eccesso, n e le sostituisce con i tag <br />. Trova anche i link interni e converte i tag di ancoraggio in tag Link. Infine, questa funzione ridimensiona le immagini utilizzando gli attributi dei tag.

  8. Per generare l’interfaccia utente principale di ogni percorso dinamico, aggiungiamo il seguente codice:
    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>
        </>
      );
    }

    Dopo aver analizzato l’HTML grezzo dai dati di Gutenberg, il codice restituisce JSX che rappresenta l’interfaccia utente formattata della pagina.

Infine, quando eseguiamo il progetto, la pagina iniziale mostrerà un elenco di post del WordPress. Inoltre, facendo clic sui singoli post, vedremo che il parse dei contenuti di Gutenberg è stato eseguito correttamente.

Distribuire il sito statico Next.js su Kinsta

Quando si combinano WordPress headless con framework all’avanguardia come Next.js, diventa essenziale trovare una soluzione di distribuzione economica, soprattutto quando si utilizza un hosting WordPress potente come quello di Kinsta per il proprio sito WordPress. Il servizio di Hosting di Siti Statici di Kinsta offre un modo semplice e conveniente per portare il proprio sito online.

Kinsta permette di ospitare gratuitamente fino a 100 siti web statici. Per prima cosa, inviamo il codice al nostro provider Git preferito (Bitbucket, GitHub o GitLab). Una volta che il repo sarà pronto, seguiamo questi passaggi per distribuire il nostro sito statico su Kinsta:

  1. Accediamo o creiamo un account per visualizzare la dashboard MyKinsta.
  2. Autorizziamo Kinsta con il nostro provider Git.
  3. Clicchiamo su Siti statici nella barra laterale di sinistra e poi su Aggiungi sito.
  4. Selezioniamo il repository e il branch da cui desideriamo effettuare il deploy.
  5. Assegniamo un nome unico al sito.
  6. Aggiungiamo le impostazioni di build nel seguente formato:
    • Comando di build: npm run build
    • Versione Node: 18.16.0
    • Directory di pubblicazione: out
  7. Infine, clicchiamo su Crea sito.

E il gioco è fatto! In pochi secondi avremo un sito distribuito. Viene fornito un link per accedere alla versione distribuita del sito. In seguito potremo eventualmente aggiungere il nostro dominio personalizzato e un certificato SSL personale.

In alternativa all’Hosting di Siti Statici, possiamo scegliere di distribuire il nostro sito statico con il servizio di Hosting di Applicazioni di Kinsta, che offre una maggiore flessibilità di hosting, una gamma più ampia di vantaggi e l’accesso a funzionalità più robuste, come la scalabilità, la distribuzione personalizzata tramite un file Docker e statistiche complete che comprendono dati storici e in tempo reale. Inoltre, non è necessario configurare il progetto Next.js per il rendering statico.

Riepilogo

Questa guida ha spiegato come integrare ed eseguire efficacemente il parse dei contenuti dei blocchi di Gutenberg come HTML tramite l’API di WordPress. Questo rende possibile il rendering di qualsiasi tipo di contenuto sul front-end quando si utilizza WordPress headless.

Potete ospitare il vostro WordPress headless sul nostro servizio di hosting WordPress gestito e distribuire il vostro sito statico sul nostro servizio di hosting per siti statici. In questo modo, tutto ciò che riguarda il vostro sito si troverà in un’unica dashboard: MyKinsta.

Scegliendo Kinsta, potete beneficiare di un provider di hosting che dà priorità alle prestazioni ottimali del sito e alla scalabilità, proteggendo al contempo i siti web con misure di sicurezza avanzate. Provate Kinsta oggi stesso!

Che ne pensate di WordPress headless e del suo rendering? Avete un modo migliore per integrare i blocchi di Gutenberg? Condividete le vostre idee nella sezione commenti!

Jeremy Holcombe Kinsta

Content & Marketing Editor at Kinsta, WordPress Web Developer, and Content Writer. Outside of all things WordPress, I enjoy the beach, golf, and movies. I also have tall people problems ;).