GeneratorsDesignDefault Theme Headers

Default Theme Headers Generator

The header images you ship with the theme, the custom-header support that makes them selectable, and the header.php markup that puts one on the page.

acme-custom-header.php
Saved just now
Bundled headers
Dunes at dawndunes
Harbour in fogharbour
The workshop benchworkshop
add_theme_support( 'custom-header' )
Support options
flex-width
Let the user keep an image wider or narrower than the declared width, with no crop step.
flex-height
The same for height — the usual choice for a single-column theme.
header-text
Show the site title and tagline over the header, with a colour control in the Customiser.
uploads
Allow the user to upload their own image as well as picking from yours.
video
Accept an MP4 or a YouTube URL as the header. Core hides it below 900px wide.
wp-head-callback
Print the chosen text colour in wp_head, and hide the title when the user turns it off.
<?php
// Generated with WP CodeKit — powered by GrowQuest (https://growquest.io).
// Add to your theme's functions.php.

/**
 * Custom header support and the images bundled with the theme.
 */
function acme_custom_header_setup() {
	add_theme_support(
		'custom-header',
		array(
			'default-image'      => get_template_directory_uri() . '/assets/headers/dunes.jpg',
			'width'              => 1920,
			'height'             => 480,
			'flex-width'         => false,
			'flex-height'        => true,
			'header-text'        => true,
			'default-text-color' => '1c1a15',
			'uploads'            => true,
			'wp-head-callback'   => 'acme_header_style',
		)
	);

	register_default_headers(
		array(
			'dunes'    => array(
				'url'           => '%1$s/assets/headers/dunes.jpg',
				'thumbnail_url' => '%1$s/assets/headers/dunes-thumb.jpg',
				'description'   => __( 'Dunes at dawn', 'acme' ),
			),
			'harbour'  => array(
				'url'           => '%1$s/assets/headers/harbour.jpg',
				'thumbnail_url' => '%1$s/assets/headers/harbour-thumb.jpg',
				'description'   => __( 'Harbour in fog', 'acme' ),
			),
			'workshop' => array(
				'url'           => '%1$s/assets/headers/workshop.jpg',
				'thumbnail_url' => '%1$s/assets/headers/workshop-thumb.jpg',
				'description'   => __( 'The workshop bench', 'acme' ),
			),
		)
	);
}
add_action( 'after_setup_theme', 'acme_custom_header_setup' );

/**
 * Print the header text colour the user chose.
 *
 * Only runs when wp-head-callback is registered above.
 */
function acme_header_style() {
	$text_color = get_header_textcolor();

	// Nothing to print only when the text is shown AND left at the default colour.
	// Bailing on the colour alone would skip the rule that hides the title.
	if ( display_header_text() && get_theme_support( 'custom-header', 'default-text-color' ) === $text_color ) {
		return;
	}

	?>
	<style id="acme-header-style">
	<?php if ( ! display_header_text() ) : ?>
		.site-title,
		.site-description {
			position: absolute;
			clip-path: inset( 50% );
		}
	<?php else : ?>
		.site-title a,
		.site-description {
			color: #<?php echo esc_attr( $text_color ); ?>;
		}
	<?php endif; ?>
	</style>
	<?php
}

About this generator

register_default_headers() Generator Online

Bundle header images with your theme and let the site owner pick one from the Customiser. This generator writes the add_theme_support( 'custom-header' ) block and the matching register_default_headers() array together, with the %1$s and %2$s URL placeholders core expects, the optional wp-head-callback that prints the chosen text colour, and the header.php code that renders the result.

The Customiser's Suggested list is mocked live from your entries, so you can see the tiles, the descriptions and the crop behaviour your declared dimensions will produce before anything is uploaded. Free, no account, and nothing you enter leaves the browser.

Reviewed Output tested on WordPress 6.8 · PHP 8.3

Why the custom header generator beats copying the Twenty-Something example

The url values in register_default_headers() are not plain URLs — core runs each one through vsprintf(), where %1$s is the parent theme URI and %2$s is the child theme URI. Hardcoding get_template_directory_uri() works right up until a child theme wants its own headers. Add a header-text control with nothing to print the colour, or a default-text-color with a # in front of it, and you have a Customiser panel that does nothing. This tool writes both blocks and audits the pair.

The %1$s and %2$s placeholders written for you

Every url and thumbnail_url is emitted against the placeholder for the base you chose — parent theme URI or child theme URI — and an absolute http:// file name is an error with a one-click fix that strips the domain.

Array keys treated as the permanent identifiers they are

Core remembers the user's choice by array key, so an unsafe key is an error with the cleaned value shown, a duplicate key is an error because the second entry replaces the first, and a default image pointing at a key that is not registered is a warning.

Crop behaviour predicted from your own numbers

Record each image's real dimensions and the generator compares them with the declared width/height and the flex flags, warning where core will crop, and noting when several images have aspect ratios different enough to make the Customiser preview jump.

The header-text pairing enforced

header-text on with no wp-head-callback means nothing prints the colour the user picks, so the control does nothing at all — a warning with a one-click fix. A default-text-color with a leading # is an error, since core adds the hash itself and you end up with ##.

A wp-head-callback that gets the condition right

The generated callback returns early only when the text is shown and left at the default colour. Bailing on the colour alone would skip the rule that visually hides the title, which is the bug in most copied versions of this function.

Thumbnails, because the Customiser will not resize for you

An entry with no thumbnail_url makes the Customiser download the full-size image for a 230px tile. Missing thumbnails are flagged, and one click derives -thumb file names from the originals.

How does the Default Theme Headers Generator work?

Four steps. The Customiser mock updates as you type, so the Suggested list you are describing is visible throughout.

  1. 01

    List the bundled headers

    Add each image with its array key, file name, thumbnail file name, description and real dimensions. Reorder them by dragging — the order here is the order of the Suggested tiles.

  2. 02

    Configure custom-header support

    Set the declared width and height, the flex flags, whether uploads and video headers are allowed, and whether the site owner gets the header text controls at all.

  3. 03

    Choose the folder and the URL base

    Point at the folder your images live in and pick %1$s for a parent theme or %2$s for a child theme. Mark one entry as the default image.

  4. 04

    Clear the checks, then export

    Resolve the flagged keys, descriptions and colour values, then take the functions.php block, the child-theme variant hooked at priority 11, or the header.php output code.

Worked example — one bundled header with the text-colour control

Support declaration and the registration array in one setup function. The %1$s in each URL is replaced by core with the parent theme URI.

/**
 * Custom header support and the images bundled with the theme.
 */
function acme_custom_header_setup() {
	add_theme_support(
		'custom-header',
		array(
			'default-image'      => get_template_directory_uri() . '/assets/headers/dunes.jpg',
			'width'              => 1920,
			'height'             => 480,
			'flex-width'         => false,
			'flex-height'        => true,
			'header-text'        => true,
			'default-text-color' => '1c1a15',
			'uploads'            => true,
			'wp-head-callback'   => 'acme_header_style',
		)
	);

	register_default_headers(
		array(
			'dunes' => array(
				'url'           => '%1$s/assets/headers/dunes.jpg',
				'thumbnail_url' => '%1$s/assets/headers/dunes-thumb.jpg',
				'description'   => __( 'Dunes at dawn', 'acme' ),
			),
		)
	);
}
add_action( 'after_setup_theme', 'acme_custom_header_setup' );

Because wp-head-callback is set, the generator also writes acme_header_style() — the function that prints the chosen colour, or the rule that visually hides the title and tagline when the user turns the header text off.

Custom header images — frequently asked questions

The questions that come up when a bundled header does not appear, or does not look right.

Why do my registered headers not appear in the Customiser?

Three usual causes. The theme has to declare add_theme_support( 'custom-header' ) before or alongside register_default_headers(), both from a callback on after_setup_theme. The image files have to exist at the path the url resolves to. And each entry needs a distinct array key — a duplicate key silently replaces the earlier entry rather than adding a second one.

What do %1$s and %2$s mean in a header url?

Core passes each url and thumbnail_url through vsprintf() with two arguments: %1$s is get_template_directory_uri(), the parent theme URI, and %2$s is get_stylesheet_directory_uri(), the child theme URI. Writing %1$s/assets/headers/dunes.jpg keeps the path correct whether the theme is used directly or as a parent. Hardcoding a full URL works until a child theme wants its own headers.

What is the difference between flex-width and flex-height?

With both false, the declared width and height are enforced: the Customiser forces every uploaded image through a crop step to exactly those dimensions. Turning on flex-height lets an image of any height through untouched while the width is still fixed, and flex-width does the reverse. With both on, the dimensions are only hints, so your CSS has to cope with whatever the site owner uploads.

Why does my header text colour render as ##1c1a15?

default-text-color takes a bare hex value with no leading hash — core adds the # itself when it prints the value. Passing #1c1a15 produces ##1c1a15, which every browser discards. The same applies to the value stored by get_header_textcolor(), so template code should print it as color: #<?php echo esc_attr( get_header_textcolor() ); ?>.

How do I let users hide the site title over the header image?

Set 'header-text' => true and register a wp-head-callback. The control appears in the Customiser, and your callback checks display_header_text(): when it returns false, print CSS that visually hides .site-title and .site-description — position them absolutely with clip-path: inset(50%) rather than using display: none, so the text stays available to screen readers.

Do custom headers work in a block theme?

Not in the same way. custom-header support and the Customiser panel belong to classic and hybrid themes; a block theme puts its header in a parts/header.html template part edited in the site editor, where the image is a Cover or Image block the user edits directly. If your theme is block-based, build the header as a template part rather than registering default headers.

Where do I paste the code from the Default Theme Headers generator?

You have three good options and one bad one. Best is a small site-specific plugin — the generator can output one for you, and it keeps working when you change theme. A code-snippets plugin is fine too. A child theme's functions.php works but ties the code to that theme. Never edit the parent theme: the next update overwrites it and your code is gone.

Can I use the generated code in client work or a commercial plugin?

Yes. The output is ordinary WordPress API calls that you configured — there is no licence attached to it and no attribution required. Use it in client sites, commercial plugins and themes, or products you sell. The generated file carries a short credit comment you are free to delete.

The other Design generators in the library, plus the WordPress tools most often used alongside this one.