WordPress 6.8: New Features and Developer Changes

WordPress 6.8 ships in April 2025 and brings several developer-facing improvements: an updated Twenty Twenty-Five theme with new patterns, enhanced block-locking controls, improved wp_navigation block performance, and a new block.json field — allowedBlocks for InnerBlocks templates. Under the hood, the REST API gains batch endpoints improvements and the Script Modules API reaches stable status.

Problem: A WordPress site is running version 6.7 and the upgrade notes for 6.8 are unclear about which changes require plugin or theme code updates versus which are automatic engine improvements.

Solution: WordPress 6.8 introduces a client-side router for block-theme navigation (no full page reload between pages using the Interactivity API), updates to the HTML API with full tag processor support for custom elements, and a new wp_admin_notice() function replacing direct admin_notices output. Review your plugin's admin_notices callbacks and any navigation-related JavaScript for compatibility.


The examples below demonstrate the new allowedBlocks restriction in block.json, the improved batch REST request format, and how to register a block with the new render_callback improvements available in 6.8.


{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "my-plugin/feature-card",
    "title": "Feature Card",
    "category": "design",
    "attributes": {
        "heading": { "type": "string", "default": "" },
        "highlighted": { "type": "boolean", "default": false }
    },
    "supports": {
        "html": false,
        "color": { "background": true, "text": true },
        "spacing": { "padding": true }
    },
    "usesContext": [ "postId", "postType" ],
    "providesContext": { "my-plugin/isHighlighted": "highlighted" },
    "editorScript": "file:./index.js",
    "style":        "file:./style.css"
}


<?php
// Register block with dynamic render_callback receiving full context (6.8)
add_action( 'init', function () {
    register_block_type( __DIR__, [
        'render_callback' => function ( array $attrs, string $content, WP_Block $block ): string {
            $post_id     = $block->context['postId']   ?? get_the_ID();
            $highlighted = (bool) ( $attrs['highlighted'] ?? false );
            $heading     = esc_html( $attrs['heading'] ?? '' );

            $class = 'feature-card' . ( $highlighted ? ' feature-card--highlighted' : '' );

            return sprintf(
                '<div class="%s"><h3>%s</h3><div class="feature-card__body">%s</div></div>',
                esc_attr( $class ),
                $heading,
                $content
            );
        },
    ] );
} );

// Batch REST API request (6.8 improved format)
// POST /wp-json/batch/v1
$batch_requests = [
    'requests' => [
        [ 'method' => 'GET',   'path' => '/wp/v2/posts?per_page=5' ],
        [ 'method' => 'GET',   'path' => '/wp/v2/categories'       ],
        [ 'method' => 'PATCH', 'path' => '/wp/v2/posts/42',
          'body'   => [ 'status' => 'publish' ]                     ],
    ],
    'validation' => 'require-all-validate',  // fail whole batch if any request fails validation
];


NOTE: The validation: require-all-validate option in batch requests is a 6.8 addition; without it the default is normal, which validates and executes each request independently — use require-all-validate when all batch items must succeed together to avoid partial-update inconsistencies.