Remove class=”wp-block-heading” From WordPress Heading Blocks

WordPress 5.9 started auto-adding class="wp-block-heading" to every <h1> … <h6> block. This filter strips it cleanly without disabling the block editor or rewriting core. Drop it in functions.php and every heading serialises as a plain <h2>.

The wp-block-heading class arrived with the Block API v2 migration to provide a style hook for block themes. If your theme already styles headings globally (most do), the class is noise — it bloats HTML, breaks existing CSS selectors that assume naked heading tags, and adds a diff line to every saved post. On gauravtiwari.org I ship around 2.1 KB of extra HTML across a long article for zero styling benefit. The filter below removes it at render time.

What this snippet does

  • Strips class="wp-block-heading" from all h1h6 output by the core heading block
  • Runs on the render_block filter so it affects published output, not stored post content
  • Does not touch heading-blocks inside patterns or third-party blocks
  • Saves roughly 20 bytes per heading — adds up fast on long-form content and archive pages
  • Keeps anchor IDs and any custom classes the author added intact

Install and use

Drop into your child theme functions.php or a mu-plugin. No settings, no admin UI. Flush any full-page cache so stored HTML gets regenerated, otherwise the old class will sit in Flying Press / WP Rocket cache files until next rebuild.

<?php
/**
 * Remove class="wp-block-heading" from core/heading block output.
 */
add_filter( 'render_block', function ( $block_content, $block ) {
    if ( 'core/heading' !== ( $block['blockName'] ?? '' ) ) {
        return $block_content;
    }
    // Remove standalone class attribute added by the block.
    $block_content = preg_replace(
        '/\sclass="wp-block-heading"/',
        '',
        $block_content
    );
    // Remove it when mixed with other classes.
    $block_content = preg_replace(
        '/(class="[^"]*?)\s?wp-block-heading\s?([^"]*?")/',
        '$1$2',
        $block_content
    );
    return $block_content;
}, 10, 2 );

How it works

The render_block filter fires for every block during render, so we scope to core/heading only and run two regexes. The first handles the common case where the block has no other classes and the attribute is exactly class="wp-block-heading". The second handles the edge case where the author added an additional class (like has-text-align-center) so we strip only the targeted class without nuking the whole attribute. Anchor IDs and custom classes survive untouched.

Download and source

FAQs

Will this break my theme styling?

Only if your CSS explicitly selects .wp-block-heading. Most themes (including Marketers Delight, Kadence, GeneratePress, and classic themes) style headings via naked tag selectors, so removing the class is safe.

Why not unset the class in block.json or edit core?

Never edit core. Overriding block.json via a filter is possible but more invasive than a render-time strip. The render_block filter is the cleanest, most reversible hook.

Does this affect TinyMCE or the Classic Editor?

No. The class is only added by the Block Editor. Classic Editor heading output uses naked tags.

What about heading blocks in synced patterns or third-party blocks?

The filter checks blockName === core/heading, so headings inside generateblocks/headline, kadence/advancedheading, or similar blocks are untouched — they use their own classes.

Is this a valid performance optimisation?

For a 3,000-word article with 20+ headings you save around 400 bytes after gzip — measurable but small. The bigger win is keeping existing CSS selectors valid when migrating an older theme to block content.

Leave a Comment