1. Startseite
  2. Bright Arboretum
  3. Contentful, Next.js, Arboretum für Content-Driven Websites

Contentful, Next.js und Arboretum für Content-Driven Websites

Jetzt, da du weißt, warum du Arboretum nutzen solltest und wie du deine Contentful-Umgebung dafür konfigurierst, ist es an der Zeit, endlich deine Inhalte zu präsentieren. Dafür brauchen wir also ein Frontend.

Arboretum ist unabhängig von der Frontend-Lösung deiner Wahl. Wenn diese JavaScript-Code ausführen kann, ist das super, denn dann kannst du einfach das Arboretum SDK verwenden. In seltenen Fällen, in denen das nicht möglich ist, musst du die Funktionen des Arboretum-SDKs selbst nachbauen.

In diesem Beispiel verwenden wir Next.js mit React, aber wenn du dich ein wenig anstrengst, solltest du die Code-Schnipsel an deinen Anwendungsfall anpassen können – egal, ob es sich um Astro, Solid, Remix, Nuxt oder etwas Ähnliches handelt.

Das Ziel ist ein Frontend, das jede in Contentful modellierte Sitemap-Struktur rendert. Im Idealfall sollten wir nichts an dieser Struktur fest codieren. Werden wir das schaffen und ist es überhaupt möglich? Finden wir es heraus.

Arboretum SDK

Zu diesem Zeitpunkt solltest du die Arboretum-App für deine Contentful-Umgebung installiert und konfiguriert haben. Falls das nicht der Fall ist, findest du in diesem Artikel eine detaillierte Anleitung.

Du benötigst außerdem ein Next.js-Projekt. Wir empfehlen, ein leeres Projekt anzulegen, um den Einstieg zu erleichtern, aber wenn du dich traust, kannst du natürlich auch ein bestehendes Projekt verwenden. In den Beispielen werden npm und TypeScript verwendet, aber du kannst in diesem Bereich wählen, was dir gefällt.

Das Arboretum SDK ist eine schlanke, aber leistungsstarke Bibliothek ohne Abhängigkeiten, die in TypeScript geschrieben wurde, um mit Contentful-APIs zu interagieren. Es läuft sowohl in einer Serverumgebung wie Node.js als auch im Browser, was es zu einer großartigen Lösung für eine Vielzahl von Anwendungsfällen macht.

Füge das Arboretum SDK mit folgendem Befehl zu deinen Abhängigkeiten hinzu:

Auf der Suche nach Unterstützung mit Arboretum oder Contentful

Meldet euch bei uns, dann schauen wir gemeinsam, wie wir das Problem lösen können. Wir kennen Arboretum und Contentful in- und auswendig.

Termin buchen

Nachdem die Installation abgeschlossen ist, ist es an der Zeit, den Arboretum-Client einzurichten. Er muss mit den Parametern erstellt werden, mit denen du deine Arboretum-App konfiguriert hast, sowie mit deinen Contentful-Anmeldedaten, damit Arboretum Abfragen durchführen kann.

Es gibt verschiedene Möglichkeiten, einen Client zu erstellen, aber am einfachsten ist es, das Konfigurationsobjekt aus dem Konfigurationsbildschirm der Arboretum-App in Contentful zu kopieren (siehe unten).

Du findest den Konfigurationsbildschirm unter Apps > Installierte Apps > Arboretum > Konfigurieren.

Du musst nur den Zugriffstoken selbst eingeben, da dieser von der Arboretum-App nicht direkt gelesen werden kann.

Du kannst den Client entweder für den Vorschau- oder den Veröffentlichungsmodus konfigurieren, indem du den Parameter „preview“ im Konfigurationsobjekt setzt. Im Vorschau-Modus nutzt Arboretum die Content Preview API (kurz CPA) und im Veröffentlichungsmodus die leistungsstärkere Content Delivery API (CDA). Stelle sicher, dass das von dir eingegebene Token Zugriff auf die von dir verwendete Umgebung hat und dass das CDA-Zugriffstoken verwendet wird, wenn „preview“ auf „false“ gesetzt ist, und umgekehrt.

Erstelle eine leere Datei an einem Ort deiner Wahl und erstelle den Client. Er sollte wie folgt aussehen:

Vielleicht ist dir aufgefallen, dass das Erstellen eines Clients asynchron abläuft. Es kann vorkommen, dass du das oberste „await“ nicht verwenden kannst oder willst. In solchen Fällen kannst du es an deine Anforderungen anpassen. Das könnte etwa so aussehen:

Arboretum-Initialisierung

Der Lebenszyklus des Arboretum-SDK ist in zwei Teile gegliedert – das Erstellen eines Clients und dessen Verwendung. Wenn eine Arboretum-Instanz erstellt wird, ruft sie aus deiner Contentful-Umgebung alle Daten ab, die sie jemals benötigen wird – alle definierten Locales, Content-Typen und alle in der Konfiguration definierten Seiteneinträge.

Die Abfragen erfolgen parallel, und wenn deine Website nicht allzu groß ist, kann dies mit nur 4 einfachen Anfragen erledigt werden. Ist deine Website größer, kann es vorkommen, dass die Anfrage zum Abrufen der Seiteneinträge über mehrere Anfragen verteilt werden muss (aufgrund der Contentful-Beschränkung von 1000 Einträgen pro Anfrage). In der Praxis ist das kein großes Problem, da dieser Vorgang nur einmal während der Lebensdauer des Client-Objekts stattfinden muss.

Sobald die Daten abgerufen sind, erstellt Arboretum eine interne Datenstruktur, die die Seitenhierarchie deiner Website abbildet, sodass es effizient mit einer Sitemap interagieren kann, ohne zusätzliche API-Aufrufe an Contentful. Ja, du hast richtig gelesen. Nachdem der Client erstellt wurde, sind alle Abfragen an Arboretum schnell und synchron.

Arboretum-Status aktualisieren

Der Client enthält eine Methode, um die neuesten Inhalte aus Contentful erneut abzurufen und seinen internen Status zu aktualisieren. Das geht so:

Inhaltsgesteuerte Sitemap

Zur Erinnerung: Das Inhaltsmodell deiner Seiten bildet einen Baum. An der Wurzel des Baums steht eine Seite, die als das ausgewählt wurde, was in der Web-Sprache als Startseite bekannt ist. Diese Seite wird dann über Verweise zur übergeordneten Seite für alle untergeordneten Seiten. Jede der untergeordneten Seiten kann wiederum eigene untergeordnete Seiten haben, und so geht der Prozess immer tiefer. Auch wenn die Verschachtelungstiefe nicht begrenzt ist, sind diese Strukturen in der Praxis meist nur wenige Ebenen tief.

Eine weitere Dimension, die bei der Erstellung der Sitemap eine wichtige Rolle spielt, ist die Lokalisierung. Wir werden diese Büchse der Pandora hier nicht öffnen, um den Fokus nicht zu verlieren. Der Kürze halber nehmen wir einen einfachen Fall mit nur einer einzigen englischen Sprachversion.

Mit Next.js eine Website mit einer inhaltsgesteuerten Sitemap verwalten

Wir werden die „catch-all“-Funktion von Next.js nutzen, um alle möglichen Seiten an einem einzigen Ort zu verwalten. So müssen wir nichts an der Sitemap-Struktur fest codieren und können diese Aufgabe an Arboretum abgeben. Genau das brauchen wir.

Erstelle eine Datei unter src/app/[[...slugs]]/page.tsx mit folgendem Inhalt:

Wir verwenden Next.js 15, wo params asynchron sind (daher der Promise-Wrapper). Wenn du noch v14 oder niedriger nutzt, solltest du ihn entfernen.

Beim Erkunden des Arboretum-SDK entdecken wir die pageByPath-Methode. Sie nimmt den Pfad der Seite als Argument und gibt die Seiten-ID als Ergebnis zurück.

Wir müssen einen vollständigen Pfad für eine Arboretum-Seite erstellen. Für unsere einzige englische Sprachversion können wir das so machen:

Wenn wir das alles zusammenfügen, erhalten wir:

Beachte, wie wir mit der Variablen „page“ umgehen. Falls es für einen bestimmten Pfad keine Seite gibt, gibt Arboretum dies als Fehler zurück. Bei Erfolg erhalten wir eine Seiten-ID, mit der wir Contentful nach dem Inhalt dieser Seite abfragen können. Wie du das machst, bleibt ganz dir überlassen, daher lassen wir diesen Teil hier weg.

Abschließende Gedanken

Dank des Arboretum SDK konnten wir mit nur wenigen Zeilen Code eine inhaltsgesteuerte Sitemap von Contentful mit Next.js nutzen.

Contentful und Next.js sind eine hervorragende Kombination sowohl für Server- als auch für serverlose Architekturen. Das einzige fehlende Element in diesem Setup ist eine effiziente Möglichkeit, Next.js-Anforderungspfade in Seiteneinträge von Contentful zu übersetzen. Mit Arboretum ist das Bild komplett. Zusammen bilden sie eine leistungsstarke Kombination, die die Erstellung schneller, vollständig dynamischer Websites ermöglicht.

Bonus: Überlegungen zur Performance und Next.js-Hosting

Wir haben untersucht, wie man den Arboretum-Client mit Next.js integriert, um eine dynamisch strukturierte Website zu erstellen, sowie wie man parallele Routen für den Vorschau-Modus implementiert. Ein wichtiges Thema bleibt jedoch noch unberührt: Performance und Caching. Da der Arboretum-Client während der Initialisierung Daten von Contentful abruft, wie können wir unnötige Neuinitialisierungen vermeiden?

Entwickler mit etwas Erfahrung in Next.js könnten in Erwägung ziehen, die unstable_cache-Funktion aus dem next/cache-Paket zu nutzen, um den Arboretum-Client zwischenzuspeichern. Leider gibt es bei diesem Ansatz zwei Hauptprobleme. Erstens unterstützt der Cache nur JSON-serialisierbare Objekte, was beim Arboretum-Client nicht der Fall ist. Zweitens gibt es Größenbeschränkungen für solche Objekte, und die Darstellung einer gesamten Sitemap kann ziemlich groß sein. Das könnte zwar ein möglicher Weg für zukünftige Arboretum-Versionen sein, ist aber derzeit nicht machbar.

Glücklicherweise gibt es einige Workarounds, um dieses Caching-Problem zu lösen. In einer einfachen Single-Server-Konfiguration können wir einen globalen In-Memory-Status nutzen, der so lange bestehen bleibt, wie die Anwendung läuft. Indem wir den Arboretum-Client im Speicher ablegen und eine einfache zeitbasierte Neuvalidierung verwenden, können wir die Leistung der Anwendung deutlich verbessern.

Auf Vercel ist die Situation aufgrund der begrenzten Lebensdauer von serverlosen Funktionen komplexer. Wenn eine serverlose Funktion beendet wird, geht die Arboretum-Client-Instanz verloren. Solange die Vercel-Funktion (die Rechen-Engine hinter dem von Vercel verwendeten serverlosen Node.js) jedoch „warm“ bleibt, kann sie dieselbe Arboretum-Client-Instanz wiederverwenden. Vercel stellt sicher, dass bei kostenpflichtigen Tarifen mindestens eine Funktionsinstanz warm bleibt, was die Startzeiten für Apps mit mäßigem Traffic verbessert. Es gibt jedoch eine Einschränkung: Wenn mehr als eine Vercel-Funktion gestartet wird, um eingehende Anfragen zu bearbeiten, kann es zu Inkonsistenzen in der zwischengespeicherten Sitemap kommen, da diese zu unterschiedlichen Zeitpunkten über die Funktionsinstanzen hinweg erstellt und aktualisiert werden.

Wenn dein Anwendungsfall dies zulässt (z. B. wenn du auf einem einzelnen Server arbeitest oder geringfügige Unterschiede in der Sitemap kein großes Problem darstellen), findest du hier ein Beispiel dafür, wie du einen globalen In-Memory-Zustand aufrechterhalten kannst, um initialisierte Arboretum-Clients zu verfolgen (dieser Ansatz wird in unserem Beispiel-Arboretum-Projekt-Repository arboretum-nextjs-example verwendet):

Mit dieser Hilfsfunktion in deinem Code musst du nicht mehr wie zuvor jedes Mal explizit einen neuen Arboretum-Client erstellen, sondern kannst einfach die arboretum-Funktion aufrufen, die einen zwischengespeicherten Client im Arbeitsspeicher wiederverwendet. Ein weiterer Vorteil dieser Hilfsfunktion ist, dass sie die Aktualisierung des Arboretum-Status – und damit auch deiner Sitemap – nach einem festgelegten Intervall automatisch übernimmt, gemäß der „stale-while-revalidate“-Strategie.

Bright Global
  • Expertise
  • Referenzen
  • Unser Ansatz
  • Partner
Bright IT
  • Expertise Übersicht
  • Architecture Audits
  • Legacy Liberation
  • Enterprise Platforms
  • B2C Commerce
  • UX/UI Design
  • B2B Commerce
  • Integrationen & Middleware
  • AI & Automation
  • Continuous Operations
  • Referenzen & Branchen
  • HDI Global
  • Swarovski Optik
  • Mischek
  • Dachstein Salzkammergut
  • Salzburg AG
  • Tyrolit
  • Vaillant
  • Philosophie
  • Direct-to-Expert
  • Nearshoring 2.0
  • Security & Compliance
  • Partner Übersicht
  • Storyblok
  • Optimizely
  • Contentful
  • Shopify
  • Medusa
  • commercetools
  • Ecosystem Integrationen
Insights
Kontakt
Bright IT
npm install @bright.global/arboretum-sdk
const config = {
  type: "cda-client-params",
  preview: false,
  contentful: {
    space: "<YOUR_SPACE_ID>",
    environment: "<YOUR_ENV>",
    accessToken: "<CDA_ACCESS_TOKEN_HERE>",
    options: {
      pageContentTypes: {
        page: {
          slugFieldId: "slug",
          titleFieldId: "slug",
          childPagesFieldId: "contentArea",
        },
      },
    },
  },
};

const { client, warnings } = await createArboretumClient(config);
export function arboretumClientFactory() {
  return createArboretumClient(config);
}
​​const { status, warnings } = await client.regenerate();
type Props = { params: Promise<{ slugs: Array<string> | undefined }> };

export default async function Page(props: Props) {
  const { slugs } = await props.params;

  return null;
}
function getPagePath(slugs: Array<string> | undefined) {
  let ret = "/en";

  if (!slugs) {
    return ret;
  }

  return ret + "/" + slugs.join("/");
}
// src/app/[[...slugs]]/page.tsx

import { arboretumClient } from "@/arboretum";
import { notFound } from "next/navigation";

type Props = { params: Promise<{ slugs: Array<string> | undefined }> };

export default async function Page(props: Props) {
  const { slugs } = await props.params;

  const path = getPagePath(slugs);

  const page = arboretumClient.pageByPath(path);

  if (page._tag !== "Right") {
    notFound();
  }

  return <span>Page id: {page.right.id}</span>;
}

function getPagePath(slugs: Array<string> | undefined) {
  let ret = "/en";

  if (!slugs) {
    return ret;
  }

  return ret + "/" + slugs.join("/");
}
// code fragment taken from src/lib/arboretum.ts from BrightIT/arboretum-nextjs-example

type GlobalArboretum = {
  client: ArboretumClientT;
  lastRegeneration: Date;
  regenerationInProgress: boolean;
};

declare const globalThis: {
  publishedArboretum: GlobalArboretum;
  previewArboretum: GlobalArboretum;
} & typeof global;

...

const arboretum = async (mode: Mode) => {
  const instance: ArboretumInstance =
    mode === "preview" ? "previewArboretum" : "publishedArboretum";
  if (globalThis?.[instance]?.client) {
    if (arboretumShouldRegenerate(mode)) {
      console.log(`Arboretum ${mode} regeneration started`);
      globalThis[instance].regenerationInProgress = true;
      // fire and forget
      globalThis[instance].client
        .regenerate()
        .then(({ warnings }) => {
          console.log(`Arboretum ${mode} regeneration finished`);
          if (warnings) {
            console.warn(`Arboretum ${mode} warnings:`);
            console.warn(warnings);
          }
          globalThis[instance] = {
            ...globalThis[instance],
            lastRegeneration: new Date(),
          };
        })
        .catch((err) => {
          console.log(`Arboretum ${mode} regeneration failed`);
          console.error(err);
        })
        .finally(() => {
          globalThis[instance].regenerationInProgress = false;
        });
    }
    return Promise.resolve(globalThis[instance].client);
  } else {
    const { client, warnings } = await arboretumClientSingleton(mode);
    if (warnings) {
      console.warn(`Arboretum ${mode} warnings:`);
      console.warn(warnings);
    }
    globalThis[instance] = {
      client: client,
      lastRegeneration: new Date(),
      regenerationInProgress: false,
    };
    return client;
  }
};
Insights
  • Rechtliches
  • Datenschutz
  • Follow us on LinkedIn
© 2026

Partners