Eigene Widgets schreiben: Klassisches Widget und moderner Block

Veröffentlicht am

· von Norbert Hofmann

Kurz gesagt: Seit WordPress 5.8 sind Widgets Blöcke. Wer heute etwas Eigenes für Seitenleiste oder Footer baut, schreibt am besten einen Block, den gibt es in klassischen und in Block-Themes. Für klassische Themes funktioniert das alte Verfahren mit einer Klasse, die WP_Widget erweitert, weiterhin. Oft reicht sogar ganz ohne Code eine Vorlage (Pattern) oder ein synchronisiertes Muster.

Was Widgets sind und wo sie heute stecken, erklärt Was sind Widgets?. Hier geht es darum, selbst eines zu bauen. Zuerst die Frage, welcher Weg zu deinem Vorhaben passt.

Welcher Weg passt?

Du willst …WegCode nötig?
einen festen Kasten (z. B. Newsletter-Hinweis mit Bild und Button) an mehreren Stellen zeigenSynchronisiertes Musternein
dynamische Inhalte ausgeben (z. B. „letzte 5 Beiträge aus Kategorie X“)erst prüfen, ob ein Core-Block reicht (Neueste Beiträge, Abfrage-Loop), sonst eigener dynamischer Blockggf. ja
eigene Logik oder externe Daten (API, eigene Tabelle) einbindenDynamischer Blockja (PHP, etwas JavaScript)
ein bestehendes klassisches Theme bedienen und kein Build-Tooling nutzenKlassisches Widget mit WP_Widgetja (nur PHP)

Ohne Code: Synchronisiertes Muster

  1. Baue den Inhalt im Editor aus Blöcken zusammen, etwa eine Gruppe mit Überschrift, Text und Button.
  2. Markiere die Gruppe, öffne das Drei-Punkte-Menü und wähle Muster erstellen.
  3. Vergib einen Namen und lass Synchronisiert eingeschaltet.
  4. Füge das Muster im Widget-Bereich (klassisches Theme) oder im Template (Block-Theme) ein. Änderungen am Muster wirken überall gleichzeitig.

Klassisches Widget mit WP_Widget

Dieser Weg funktioniert nur in Themes mit Widget-Bereichen (klassische Themes). Im Widget-Editor erscheint dein Widget dort als „Legacy-Widget“. Leg den folgenden Code als eigenes kleines Plugin an, etwa unter wp-content/plugins/wpde-hallo-widget/wpde-hallo-widget.php, und aktiviere es. Nicht in die functions.php des Themes, sonst ist das Widget bei einem Theme-Wechsel weg.

<?php
/**
 * Plugin Name: WPDE Hallo-Widget
 * Description: Beispiel für ein klassisches Widget.
 * Version:     1.0.0
 */

defined( 'ABSPATH' ) || exit;

class WPDE_Hallo_Widget extends WP_Widget {

    public function __construct() {
        parent::__construct(
            'wpde_hallo',                       // Basis-ID
            'WPDE Hallo',                       // Name im Backend
            array(
                'description'                 => 'Zeigt einen Gruß und die Anzahl der Beiträge.',
                'show_instance_in_rest'       => true, // Vorschau im Block-Widget-Editor
                'customize_selective_refresh' => true,
            )
        );
    }

    // Ausgabe im Frontend
    public function widget( $args, $instance ) {
        $title = ! empty( $instance['title'] ) ? $instance['title'] : '';
        $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );

        echo $args['before_widget'];
        if ( $title ) {
            echo $args['before_title'] . esc_html( $title ) . $args['after_title'];
        }
        $anzahl = (int) wp_count_posts()->publish;
        printf( '<p>Hallo! Hier gibt es schon %d Beiträge.</p>', $anzahl );
        echo $args['after_widget'];
    }

    // Formular im Backend
    public function form( $instance ) {
        $title = isset( $instance['title'] ) ? $instance['title'] : '';
        ?>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">Titel:</label>
            <input class="widefat" type="text"
                id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
                name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
                value="<?php echo esc_attr( $title ); ?>">
        </p>
        <?php
    }

    // Speichern und säubern
    public function update( $new_instance, $old_instance ) {
        $instance          = array();
        $instance['title'] = sanitize_text_field( $new_instance['title'] ?? '' );
        return $instance;
    }
}

add_action( 'widgets_init', function () {
    register_widget( 'WPDE_Hallo_Widget' );
} );

Die vier Bausteine sind immer gleich: __construct() registriert ID und Namen, widget() erzeugt die Ausgabe, form() das Formular im Backend und update() säubert die Eingaben vor dem Speichern. before_widget, before_title und so weiter kommen vom Theme, damit dein Widget zum Design passt. Wie ein Theme solche Bereiche bereitstellt, steht in Wie kann ich mein Theme widget-fähig machen?.

Moderner Weg: Eigener dynamischer Block

Ein Block funktioniert überall: im Beitrag, im Widget-Bereich klassischer Themes und im Website-Editor von Block-Themes. „Dynamisch“ heißt, dass PHP die Ausgabe bei jedem Seitenaufruf neu erzeugt, genau wie beim klassischen Widget. Das Grundgerüst erzeugt das offizielle Werkzeug create-block (Node.js nötig):

cd wp-content/plugins
npx @wordpress/create-block@latest wpde-hallo-block --variant dynamic
cd wpde-hallo-block
npm start

Danach aktivierst du das Plugin „Wpde Hallo Block“ und findest den Block im Inserter. Die Ausgabe im Frontend steht in src/render.php. Diese Datei ersetzt, was beim klassischen Widget widget() war:

<?php
// src/render.php - verfügbar: $attributes, $content, $block
$anzahl = (int) wp_count_posts()->publish;
?>
<div <?php echo get_block_wrapper_attributes(); ?>>
    <p>Hallo! Hier gibt es schon <?php echo esc_html( $anzahl ); ?> Beiträge.</p>
</div>

Einstellungen wie der Titel werden als attributes in der block.json definiert und in src/edit.js mit Komponenten wie TextControl in der Seitenleiste des Editors bearbeitet. Für die Veröffentlichung baust du mit npm run build. Den Ordner node_modules lädst du nicht auf den Server.

Für Fortgeschrittene: Klassisches Widget in einen Block überführen

  • Logik auslagern: Schreib die Ausgabe in eine eigene Funktion, etwa wpde_hallo_markup( $title ). Dann rufen WP_Widget::widget() und render.php dieselbe Funktion auf, und beide Varianten bleiben parallel nutzbar.
  • Umwandeln anbieten: Mit einer transforms-Definition vom Typ block mit blocks: [ 'core/legacy-widget' ] und dem Filter widget_types_to_hide_from_legacy_widget_block kannst du Nutzern anbieten, ein vorhandenes Legacy-Widget in deinen Block umzuwandeln.
  • Sicherheit: Jede Ausgabe escapen (esc_html(), esc_attr(), esc_url()), jede Eingabe säubern. Bei Blöcken gilt das auch für $attributes, denn die kommen aus dem gespeicherten Inhalt.
  • Performance: Aufwändige Abfragen oder API-Aufrufe in render.php mit einem Transient zwischenspeichern, weil der Code bei jedem Seitenaufruf läuft.

Einen Einstieg in die Plugin-Programmierung insgesamt findest du unter Wo finde ich eine Anleitung zur Plugin-Programmierung?, den Überblick über Plugins im Hub WordPress-Plugins.

Passend dazu: widgets_init ist einer von vielen Hooks. Eine Übersicht mit Ladereihenfolge findest du in WordPress-Hooks: Übersicht aller Actions und Filter.

Frage dazu? Hängst du an deinem Widget oder Block? Stell deinen Code im WPDE-Forum unter „Plugins“ vor.

Häufige Fragen

Mein Widget erscheint nicht in der Auswahl. Was fehlt?

Prüfe, ob das Plugin aktiv ist und register_widget() im Hook widgets_init läuft. In Block-Themes gibt es keinen Widget-Bereich, dort erscheinen klassische Widgets gar nicht. Dann brauchst du einen Block.

Kann ich den alten Widget-Editor zurückholen?

Ja, in klassischen Themes mit dem offiziellen Plugin „Classic Widgets“ oder per add_filter( 'use_widgets_block_editor', '__return_false' );. Langfristig ist der Umstieg auf Blöcke aber der zukunftssichere Weg.

Muss ich für einen Block React können?

Für einen dynamischen Block nur sehr wenig. Die Ausgabe schreibst du in PHP, im Editor genügen ein paar vorgefertigte Komponenten. create-block liefert alles lauffähig vor.

Quellen

Über den Autor: Norbert Hofmann

Norbert betreibt das WPDE-Forum und hat jahrelang im technischen Support eines Webhosters täglich WordPress-Seiten betreut. Heute arbeitet er als Cyber Defense Analyst, hat Sicherheitslücken in WordPress-Plugins gefunden (veröffentlichte CVEs) und engagiert sich mit deepfake.de gegen Betrug mit Deepfakes. Mehr auf norberthofmann.com.

Weitere Antworten