WordPress add_theme_support(): title-tag, post-thumbnails, html5, align-wide Complete Guide

WordPress’s add_theme_support() function is the theme’s declaration to the core that it opts into specific features. Without these declarations, core features that require theme cooperation are simply inactive — there will be no <title> tag in the document head, no featured image support, no wide-aligned block support, and no custom logo picker in the Customizer. The function must be called inside the after_setup_theme hook (not init), because theme setup runs before init and some features — particularly html5 and title-tag — affect output that is generated before init fires. Correctly declaring theme support is the first step in building any production WordPress theme and has implications for accessibility, SEO, and the block editor.

Problem: A custom theme has no <title> tag in its source, featured images do not appear in the block editor sidebar, the Customizer has no Custom Logo option, and wide/full-width blocks are constrained to the content width.

Solution: Add all required add_theme_support() calls inside after_setup_theme. Each feature needs its own call with the correct argument structure.

<?php
// functions.php — must be in after_setup_theme, NOT init
add_action( 'after_setup_theme', 'my_theme_setup' );

function my_theme_setup(): void {

    // ── 1. Dynamic <title> tag — replaces hardcoded <title> in header.php ─
    add_theme_support( 'title-tag' );

    // ── 2. Featured images (post thumbnails) ────────────
    add_theme_support( 'post-thumbnails' );
    // Enable for specific post types only:
    // add_theme_support( 'post-thumbnails', [ 'post', 'product' ] );

    // ── 3. HTML5 semantic markup for core elements ────────────
    add_theme_support( 'html5', [
        'search-form',
        'comment-form',
        'comment-list',
        'gallery',
        'caption',
        'style',    // outputs <style> without type attribute
        'script',   // outputs <script> without type attribute
    ] );

    // ── 4. Custom logo in the Customizer ────────────
    add_theme_support( 'custom-logo', [
        'height'      => 80,
        'width'       => 250,
        'flex-height' => true,
        'flex-width'  => true,
        'header-text' => [ 'site-title', 'site-description' ], // hide when logo set
    ] );

    // ── 5. Wide and full-width block alignment ────────────
    add_theme_support( 'align-wide' );

    // ── 6. Responsive embeds (wraps iframes for CSS aspect-ratio) ────
    add_theme_support( 'responsive-embeds' );

    // ── 7. Disable core block patterns (if you supply your own) ──────
    // remove_theme_support( 'core-block-patterns' );

    // ── 8. Editor styles — load CSS into the block editor ────────────
    add_theme_support( 'editor-styles' );
    add_editor_style( 'assets/editor-style.css' );

    // ── 9. Custom background ────────────
    add_theme_support( 'custom-background', [
        'default-color' => 'ffffff',
    ] );

    // ── 10. Post formats ────────────
    add_theme_support( 'post-formats', [
        'aside', 'image', 'video', 'quote', 'link', 'gallery',
    ] );

    // ── 11. Automatic feed links in <head> ────────────
    add_theme_support( 'automatic-feed-links' );

    // ── 12. Starter content for new theme previews ────────────
    add_theme_support( 'starter-content', [
        'posts' => [ 'home', 'about', 'contact' ],
    ] );

    // ── Translation readiness ────────────
    load_theme_textdomain( 'my-theme', get_stylesheet_directory() . '/languages' );
}

// ── Output the custom logo in templates ────────────
// In header.php:
the_custom_logo(); // outputs <a><img></a> with proper classes

// Check if a logo has been set:
if ( has_custom_logo() ) {
    the_custom_logo();
} else {
    echo '<span class="site-title">' . esc_html( get_bloginfo( 'name' ) ) . '</span>';
}

NOTE: current_theme_supports() lets you check at runtime whether a feature is active — useful in plugins that need to behave differently depending on what the active theme supports. If you are building a plugin that adds block editor features, always check current_theme_supports( 'align-wide' ) before offering wide-alignment controls, or the UI will confuse users on themes that do not support wide blocks. The title-tag support is all-or-nothing — once you declare it, you must remove the hardcoded <title> tag from header.php, or the page will have two title elements.