By default WordPress categories and tags only work for posts. Pages have no Categories or Tags panel in the editor, and the category and tag archives only list posts.
This snippet adds categories and tags to WordPress pages and shows those pages in the category and tag archives, mixed with your posts.
The snippet
<?php
/**
* Categories and tags for pages, and pages in category and tag archives.
*/
add_action( 'init', 'dpc_page_categories_tags_register' );
function dpc_page_categories_tags_register() {
register_taxonomy_for_object_type( 'category', 'page' );
register_taxonomy_for_object_type( 'post_tag', 'page' );
}
add_action( 'pre_get_posts', 'dpc_page_categories_tags_archives' );
function dpc_page_categories_tags_archives( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( ! $query->is_category() && ! $query->is_tag() ) {
return;
}
$post_types = $query->get( 'post_type' );
if ( 'any' === $post_types ) {
return;
}
$post_types = empty( $post_types ) ? array( 'post' ) : (array) $post_types;
if ( ! in_array( 'page', $post_types, true ) ) {
$post_types[] = 'page';
}
$query->set( 'post_type', $post_types );
}
How it works
register_taxonomy_for_object_type()attaches the built-incategoryandpost_tagtaxonomies to thepagepost type oninit. The Categories and Tags panels then appear in the page editor, and the columns appear in Pages → All Pages.- Attaching the taxonomy is not enough for the archives: a category or tag archive queries only posts. The
pre_get_postscallback addspageto the post types of the main query on category and tag archives. - If the archive query already has post types (another plugin added a custom post type, for example) the snippet adds
pageto them instead of replacing them. A query foranypost type is left as it is. - It skips wp-admin and every secondary query, so widgets, blocks and related-post lists that query a category keep showing posts only.
Add it with Scripts Organizer
In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Everywhere, tick PHP under Script location, paste the code into the PHP editor and publish. Leave Action/Hook empty. Both hooks fire after Scripts Organizer has loaded the code.
The same code also works unchanged in a small plugin or an mu-plugin.
Customise it
- Only categories: remove the
post_tagline and theis_tag()check. - A custom post type instead of pages: replace
'page'with your post type slug in both functions. - Remove the
pre_get_postspart if you only want to group pages in the editor and keep the archives for posts.
Gotchas
- A category or tag with only pages in it still counts them in the term count, so it shows up in category lists and widgets.
- Your theme’s archive template may show a date or author for each item. Pages have both, but your design may not expect them in a post list.
- A Query Loop block set to inherit the query from the template shows the pages too. A Query Loop with its own settings does not.
In the Scripts Organizer library Import it with one click from Scripts Organizer.