theme_templatesfilter-hookWP 4.9.6

Allows adding/removing page (post) template files used in the template selector when editing a post.

This filter is common to all post types. To avoid checking the post type in your code, you can use the similar theme_(post_type)_templates filter.

Usage

add_filter( 'theme_templates', 'wp_kama_theme_templates_filter', 10, 4 );

/**
 * Function for `theme_templates` filter-hook.
 * 
 * @param string[]     $post_templates Array of template header names keyed by the template file name.
 * @param WP_Theme     $theme          The theme object.
 * @param WP_Post|null $post           The post being edited, provided for context, or null.
 * @param string       $post_type      Post type to get the templates for.
 *
 * @return string[]
 */
function wp_kama_theme_templates_filter( $post_templates, $theme, $post, $post_type ){

	// filter...
	return $post_templates;
}
$post_templates(array)

An array of post template files. The array key is the file path relative to the theme directory, and the value is the template file name.

Array(
	[page-my-tpl.php] => My great template in the theme root
	[template/page-default.php] => My default template in the template directory
	[template/portfolio/page-full.php] => Full-width portfolio template in template -> portfolio
	[template/portfolio/page-mini.php] => Minimal portfolio template in template -> portfolio
)
$this(WP_Theme)
An instance of the WP_Theme class.
$post(WP_Post/null)
The post object (when editing) or null.
$post_type(string)
The post type for which the template list is requested.

Examples

#1 Add a custom template to the list for all post types

By default, WordPress looks for templates in the theme root and first-level directories. Let us add a custom template from a more deeply nested directory.

We add the template without checking the post type, so it will be available to all post types.

add_filter( 'theme_templates', 'add_my_template_to_list', 10, 4 );

function add_my_template_to_list( $templates, $wp_theme, $post, $post_type ) {
	$templates['template/defaults/page.php'] = 'My default template';

	return $templates;
}

#2 Add a custom template to the list for pages

Add our template to the templates array only when the list is requested for pages: post_type=page.

add_filter( 'theme_templates', 'add_my_template_to_list', 10, 4 );

function add_my_template_to_list( $templates, $wp_theme, $post, $post_type ) {
	if ( 'page' === $post_type ) {
		$templates['template/defaults/page.php'] = 'My default page template';
	}

	return $templates;
}

If you use another post type, replace page with the required type. Alternatively, use the similar theme_(post_type)_templates hook to avoid the check in your code.

#3 Add a custom template from a plugin

Suppose we need a page with a custom template that must be added by a plugin. Create a plugin with the following structure:

plugin-my-tpl (plugin directory)
├── plugin-my-tpl.php (main plugin file)
└── templates (templates directory)
		├── page-tpl-1.php (template #1)
		└── page-tpl-2.php (template #2)
The plugin-my-tpl.php file
/**
 * Plugin Name: plugin-my-tpl
 */

add_filter( 'theme_templates', 'add_my_template_to_list', 10, 4 );
add_filter( 'template_include', 'my_plugin_template_include' );

// Add our custom templates to the page template list
function add_my_template_to_list( $templates, $wp_theme, $post, $post_type ) {
	if ( 'page' === $post_type ) {
		// Extend the templates array with our own templates
		$templates += my_plugin_templates();
	}

	return $templates;
}

// Build the templates array
function my_plugin_templates() {
	$base_path = basename( __DIR__ );

	return [
		$base_path . '/templates/page-tpl-1.php' => 'Template from plugin #1',
		$base_path . '/templates/page-tpl-2.php' => 'Template from plugin #2',
	];
}

// Load a page template from the plugin
function my_plugin_template_include( $template ) {
	// If this is not a page, return the existing value
	if ( ! is_page() ) {
		return $template;
	}

	// Get the saved template
	$path_slug = get_post_meta( get_the_ID(), '_wp_page_template', true );

	// If this is not a plugin template, return the existing value
	if ( ! in_array( $path_slug, array_keys( my_plugin_templates() ) ) ) {
		return $template;
	}

	// Build the full file path
	$path_file = wp_normalize_path( WP_PLUGIN_DIR . '/' . $path_slug );

	// Check whether the template file physically exists and pass it to the engine
	if ( file_exists( $path_file ) ) {
		return $path_file;
	}

	return $template;
}

For an object-oriented solution, see Add Page Templates to WordPress with a Plugin.

Changelog

Since 4.9.6 Introduced.

Where the hook is called

WP_Theme::get_page_templates()
theme_templates
wp-includes/class-wp-theme.php 1429
$post_templates = (array) apply_filters( 'theme_templates', $post_templates, $this, $post, $post_type );

Where the hook is used in WordPress

Usage not found.