WordPress

Custom Blocks entwickeln: Eigene Gutenberg-Blöcke mit React erstellen

Gutenberg-Blöcke lassen sich nicht nur nutzen, sondern auch selbst bauen. Dieser Artikel erklärt, warum eigene Blöcke den Unterschied machen und wie man mit der nativen WordPress Block API und React einen vollständigen Custom Block aufsetzt.

von Oli Feiler · 16. Juli 2025

Warum eigene Blöcke?

Der Gutenberg-Editor hat WordPress grundlegend verändert. Redakteure arbeiten visuell, Inhalte entstehen blockweise — und das funktioniert gut, solange der eingebaute Block-Vorrat das abbildet, was das Projekt tatsächlich braucht. Irgendwann tut er das nicht mehr.

Wer dann mit dem Absatz-Block keine CI-konforme Infobox bauen kann, löst das oft mit einem Plugin. Das Plugin bringt zwanzig Blöcke, von denen drei gebraucht werden, und aktualisiert sich beim nächsten WordPress-Major gerne auf eine Version, die irgendetwas bricht. Eigene Blöcke sind das Gegenprogramm: einmal gebaut, exakt auf das Projekt zugeschnitten, vollständig unter Kontrolle des Entwicklungsteams.

Custom Blocks sind redaktionelles Interface-Design. Sie übersetzen Gestaltung, Marke und Governance in konkrete Bedienbarkeit. Ein Custom Block lässt nur zu, was vorgesehen ist — kein freigestaltetes Markup, keine Farbabweichungen vom Brand, keine „ich hab den Block kurz umgebaut"-Situationen.

Zwei Ansätze, ein Ergebnis

Es gibt zwei verbreitete Wege, eigene Blöcke in WordPress zu bauen.

Der erste ist der PHP-zentrierte Weg mit ACF: Advanced Custom Fields Pro erlaubt es, Blöcke als PHP-Templates zu definieren. Felder werden im ACF-Interface gepflegt, das Template rendert sie im Frontend. Das kommt ohne React-Build-Pipeline aus und ist gut für Teams, die tief in PHP zuhause sind, aber keinen JavaScript-Build-Prozess aufsetzen wollen.

Der zweite ist die native WordPress Block API: Blöcke entstehen als React-Komponenten, werden über block.json registriert und mit dem offiziellen @wordpress/create-block-Scaffold aufgesetzt. Das ist aufwendiger im Setup, aber unabhängig von Drittanbietern, direkt in Core-Konzepte integriert und auf Dauer das stabilere Fundament.

Dieser Artikel folgt dem nativen Weg.

Voraussetzungen

  • Node.js Active LTS, aktuell mindestens 20.10.0, sowie npm ab 10.2.3
  • Ein lokales WordPress (z.B. LocalWP, DDEV oder wp-env )
  • PHP ≥ 8.0
  • Grundkenntnisse in React und JSX

Das Beispiel baut eine Infobox — ein klassischer Use-Case: eine Überschrift, ein Fließtext, visuell vom normalen Absatz abgesetzt.

Block-Projekt anlegen

Mit @wordpress/create-block entsteht ein vollständig aufgesetzter Block als Plugin-Ordner:

            npx @wordpress/create-block@latest infobox --namespace mein-projekt
cd infobox
npm start
        

Der --namespace-Parameter setzt den eindeutigen Bezeichner des Blocks. Empfehlenswert ist der Projekt- oder Agenturname, um Konflikte mit anderen Plugins zu vermeiden. Der Block heißt dadurch intern mein-projekt/infobox — und aus dieser Kombination leitet WordPress später auch die automatisch generierte CSS-Klasse ab.

Nach npm start läuft ein Watcher, der Änderungen an den Quelldateien automatisch kompiliert. Der Ordner hat folgende Struktur:

            infobox/
├── src/
│   ├── block.json       ← Metadaten und Attribute
│   ├── index.js         ← Registrierung
│   ├── edit.js          ← Editor-Ansicht (React)
│   ├── save.js          ← Frontend-Output (React)
│   ├── editor.scss      ← Editor-spezifische Styles
│   └── style.scss       ← Frontend-Styles (auch im Editor geladen)
├── build/               ← Kompiliertes Bundle (nicht manuell bearbeiten)
├── infobox.php          ← Plugin-Einstiegspunkt
└── package.json
        

block.json: Die Konfiguration

block.json ist das zentrale Konfigurationsfile. Hier werden Metadaten, Attribute und Assets des Blocks beschrieben:

            {
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "mein-projekt/infobox",
  "version": "0.1.0",
  "title": "Infobox",
  "category": "text",
  "description": "Hervorgehobene Infobox mit Überschrift und Fließtext.",
  "supports": {
    "html": false
  },
  "attributes": {
    "heading": {
      "type": "string",
      "source": "html",
      "selector": "h3",
      "default": ""
    },
    "content": {
      "type": "string",
      "source": "html",
      "selector": "p",
      "default": ""
    }
  },
  "editorScript": "file:./index.js",
  "editorStyle": "file:./index.css",
  "style": "file:./style-index.css"
}
        

„supports“ { „html“: false } verhindert, dass Redakteure in den HTML-Modus wechseln und das Block-Markup manuell überschreiben können; eine einfache, aber wirkungsvolle Absicherung.

Block Supports lassen sich weit ausbauen: Hintergrundfarben, Typografie, Abstände. Wer sie aktiviert, sollte sie aber über theme.json auf eine definierte Palette begrenzen, sonst öffnet der Block genau die Tür, die der Artikel vorher schließen will.

Die attributes beschreiben alle Felder, die der Block speichert. Über source und selector legt WordPress fest, aus welchem Teil des gespeicherten HTMLs die Werte beim erneuten Laden gelesen werden.

edit.js: Der Block im Editor

edit.js definiert, was Redakteure im Gutenberg-Editor sehen und bearbeiten. Die Funktion bekommt attributes (aktueller Zustand) und setAttributes (Setter) übergeben:

            import { useBlockProps, RichText, InspectorControls } from '@wordpress/block-editor';
import { PanelBody } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

export default function Edit({ attributes, setAttributes }) {
  const { heading, content } = attributes;

  // useBlockProps hängt automatisch die kanonische Block-Klasse
  // (.wp-block-mein-projekt-infobox) sowie Gutenberg-Accessibility-Attribute an
  const blockProps = useBlockProps();

  return (
    <>
      {/* InspectorControls erscheint in der rechten Sidebar des Editors */}
      <InspectorControls>
        <PanelBody title={ __('Infobox-Einstellungen', 'infobox') }>
          <p>Hier lassen sich Varianten, Farben oder Icons ergänzen.</p>
        </PanelBody>
      </InspectorControls>

      <div { ...blockProps }>
        <RichText
          tagName="h3"
          value={ heading }
          onChange={ (val) => setAttributes({ heading: val }) }
          placeholder={ __('Überschrift …', 'infobox') }
        />
        <RichText
          tagName="p"
          value={ content }
          onChange={ (val) => setAttributes({ content: val }) }
          placeholder={ __('Inhalt …', 'infobox') }
        />
      </div>
    </>
  );
}
        

useBlockProps() ergänzt das Root-Element automatisch um die block-spezifische CSS-Klasse — bei name: „mein-projekt/infobox“ ist das .wp-block-mein-projekt-infobox. Zusätzliche Klassen lassen sich als Parameter übergeben, die kanonische Klasse entsteht aber immer aus Namespace und Blockname.

RichText rendert kein separates Input-Feld, Redakteure schreiben direkt im Block. Das Ergebnis fühlt sich an wie nativer Gutenberg-Inhalt, weil es das ist.

save.js: Der Frontend-Output

save.js beschreibt das finale HTML, das in der Datenbank gespeichert und im Frontend ausgegeben wird. Kein State, keine Event-Handler, eine reine Render-Funktion:

            import { useBlockProps, RichText } from '@wordpress/block-editor';

export default function Save({ attributes }) {
  const { heading, content } = attributes;
  const blockProps = useBlockProps.save();

  return (
    <div { ...blockProps }>
      <RichText.Content tagName="h3" value={ heading } />
      <RichText.Content tagName="p" value={ content } />
    </div>
  );
}
        

Das Ergebnis ist statisches HTML — kein zusätzliches JavaScript im Frontend, kein unnötiger Runtime-Overhead.

Wichtig: Die Struktur von save.js darf nach dem ersten Produktiveinsatz nicht mehr verändert werden, ohne eine Block Deprecation zu definieren. WordPress prüft beim Laden jedes gespeicherten Blocks, ob das hinterlegte HTML mit der aktuellen save()-Funktion übereinstimmt. Abweichungen führen zum „Block ist ungültig"-Fehler im Editor.

Als Plugin aktivieren

Der erzeugte Ordner ist bereits ein vollständiges WordPress-Plugin. Er wird in wp-content/plugins/ abgelegt und im Backend aktiviert. Der Einstiegspunkt infobox.php registriert den Block über:

            function mein_projekt_infobox_block_init() {
    register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'mein_projekt_infobox_block_init' );
        

register_block_type liest alle Informationen direkt aus build/block.json: Attribute, Assets, Editor-Script. Manuelle wp_register_script()-Aufrufe sind nicht nötig.

Für den Produktionsbuild:

            npm run build
        

Das erzeugt optimierte, minifizierte Dateien im build/-Ordner.

Styles einbinden

style.scss wird sowohl im Editor als auch im Frontend geladen, ideal für die grundlegenden Block-Styles. Die Klasse entspricht dem automatisch generierten Muster aus Namespace und Blockname:

            .wp-block-mein-projekt-infobox {
  background: #f0f4f8;
  border-left: 4px solid #005fa3;
  padding: 1.25rem 1.5rem;
  border-radius: 4px;

  h3 {
    margin: 0 0 0.5rem;
    font-size: 1rem;
    font-weight: 600;
  }

  p {
    margin: 0;
    color: #333;
  }
}
        

editor.scss ist für reine Editor-Overrides reserviert — z.B. wenn ein Block im Frontend keinen Rahmen haben soll, im Editor aber visuell klarer abgegrenzt sein soll.

Was als nächstes kommt

Dieser Block ist funktional, aber ausbaufähig. Typische nächste Schritte: Farbvarianten über ein variant-Attribut („info“, „warning“, „success“), Icons über @wordpress/icons, BlockControls für Alignment-Optionen in der Block-Toolbar oder InnerBlocks, wenn der Block selbst weitere Blöcke aufnehmen soll.

Die vollständige Referenz liegt unter developer.wordpress.org/block-editor — versioniert, nah an Core und zuverlässiger als jedes Tutorial.

Dass WordPress diesen Ansatz im Editor konsequent verfolgt, überrascht nicht. Das Prinzip strukturierter Inhaltselemente ist älter als Gutenberg. In unserem eigenen CMS Atomic gibt es sie von Anfang an. Damals hießen sie Content-Container, heute heißen sie Blöcke. Der Vorteil ist derselbe: Redakteure bauen Seiten aus definierten Bausteinen, konsistent und responsiv, ohne Markup anfassen zu müssen.

Ein Custom Block ist am Ende mehr als ein React-Komponenten-Paar, das in WordPress lebt. Er ist eine Entscheidung darüber, welche Freiheit Redakteure brauchen — und welche Freiheit ein Projekt besser gar nicht erst anbietet. Wer edit.js, save.js und block.json verstanden hat, baut nicht nur Gutenberg-Erweiterungen. Er gestaltet redaktionelle Systeme.

Hinweis

Dieser Text wurde zuletzt am 8. Juni 2026 aktualisiert.

Jetzt weiterlesen...

Eigene Blöcke für Ihr WordPress-Projekt

urbanstudio entwickelt Custom Blocks für WordPress und atomic, die exakt zur CI passen. Vom Konzept bis zur Integration ins bestehende System in wenigen Stunden.
Marian Feiler, Projektmanager