WordPress stores the page template assignment as a post meta value under the key _wp_page_template. When an editor selects a custom template from the “Page Attributes” sidebar in Gutenberg, the filename of that template (relative to the theme directory, e.g. templates/full-width.php) is saved to post meta. This means you can query all pages using a specific template with WP_Query‘s meta_query parameter — just as you would query any other post meta value. This is useful before removing or renaming a template file (find out which pages would break), when applying bulk changes to pages that share a layout, or when building an admin diagnostic tool that maps templates to page URLs across a large site.
Problem: You want to rename a page template file in your theme but need to know which pages are currently using it — to update them before removing the old file and avoid broken layouts.
Solution: Query wp_postmeta for _wp_page_template using WP_Query with a meta_query. The meta value is the template's file path relative to the theme root (e.g. page-templates/full-width.php).
<?php
/**
* Get all pages using a specific page template.
*
* @param string $template_file Template filename relative to the theme root,
* e.g. 'page-templates/full-width.php'.
* @return WP_Post[]
*/
function get_pages_using_template( $template_file ) {
return get_posts( [
'post_type' => 'page',
'post_status' => 'publish',
'posts_per_page' => -1,
'meta_query' => [ [
'key' => '_wp_page_template',
'value' => sanitize_text_field( $template_file ),
] ],
'no_found_rows' => true,
] );
}
// Usage: list all pages using 'page-templates/full-width.php'
$pages = get_pages_using_template( 'page-templates/full-width.php' );
foreach ( $pages as $page ) {
printf(
'<p>ID %d — <a href="%s">%s</a> | <a href="%s">Edit</a></p>',
$page->ID,
esc_url( get_permalink( $page->ID ) ),
esc_html( get_the_title( $page->ID ) ),
esc_url( get_edit_post_link( $page->ID ) )
);
}
Get a full inventory of all templates currently in use across the site:
<?php
function get_all_templates_in_use() {
global $wpdb;
return $wpdb->get_results(
"SELECT meta_value AS template, COUNT(*) AS page_count
FROM {$wpdb->postmeta} pm
INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID
WHERE pm.meta_key = '_wp_page_template'
AND p.post_type = 'page'
AND p.post_status = 'publish'
AND pm.meta_value != 'default'
GROUP BY meta_value
ORDER BY page_count DESC"
);
}
// Example output:
// [ { template: 'page-templates/full-width.php', page_count: '12' }, ... ]
NOTE: Pages using the default template have _wp_page_template set to 'default' (the string literal), not an empty string or the path to page.php. The template path stored in meta is relative to the theme directory as WordPress resolves it at save time — it includes subdirectory prefixes like templates/ or page-templates/ if that's where your template file lives. If you can't find pages for a template, print the raw meta value with get_post_meta( $page_id, '_wp_page_template', true ) to confirm the exact string stored.