WordPress Theme Setup: The after_setup_theme Hook

Almost every WordPress theme has a setup function hooked to after_setup_theme. This is the right place to declare what your theme supports — post thumbnails, HTML5 markup, title tags, custom logos, and more. Running this code at after_setup_theme ensures WordPress knows about your theme’s capabilities before it starts rendering anything.

Problem: How do you correctly register theme features — such as post thumbnails, custom menu locations, HTML5 support, and title tag management — in a WordPress theme?

Solution: Place all feature declarations inside an after_setup_theme action hook. It fires after WordPress has loaded the theme but before headers are sent, making it the correct place for add_theme_support() and register_nav_menus() calls.

add_action( 'after_setup_theme', 'mytheme_setup' );

function mytheme_setup() {

    // Let WordPress manage the document title automatically
    add_theme_support( 'title-tag' );

    // Enable support for Post Thumbnails on posts and pages
    add_theme_support( 'post-thumbnails' );
    set_post_thumbnail_size( 1200, 628, true ); // hard-crop

    // Register additional image sizes
    add_image_size( 'blog-card',  480, 320, true );
    add_image_size( 'hero',      1600, 600, true );

    // HTML5 markup for search form, comment form, galleries, captions
    add_theme_support( 'html5', [
        'search-form',
        'comment-form',
        'comment-list',
        'gallery',
        'caption',
    ] );

    // Custom logo support
    add_theme_support( 'custom-logo', [
        'height'      => 80,
        'width'       => 250,
        'flex-width'  => true,
        'flex-height' => true,
    ] );

    // Custom background colour/image
    add_theme_support( 'custom-background', [
        'default-color' => 'ffffff',
    ] );

    // Make theme available for translation
    load_theme_textdomain( 'mytheme', get_template_directory() . '/languages' );

    // Register navigation menus
    register_nav_menus( [
        'primary' => __( 'Primary Menu', 'mytheme' ),
        'footer'  => __( 'Footer Menu',  'mytheme' ),
    ] );
}

To display the custom logo in a template:

if ( function_exists( 'the_custom_logo' ) && has_custom_logo() ) {
    the_custom_logo();
} else {
    echo '<a href="' . esc_url( home_url( '/' ) ) . '">' . esc_html( get_bloginfo( 'name' ) ) . '</a>';
}

NOTE: Always check with current_theme_supports() before using a feature that requires add_theme_support(). Child themes inherit the parent's after_setup_theme callback, so you only need to add or override what is different — not redefine everything from scratch.