Set a page for your custom post type archive, just like you would for your posts.
Assign any WordPress page as the archive page for a custom post type — just like the native “page for posts” setting.
WordPress custom post type archives are dynamically generated and can’t be edited as regular pages. This creates recurring issues:
Several plugins have attempted to solve this:
While these provided inspiration, none fully replicate WordPress’s native behavior.
This plugin mimics how WordPress handles the posts page (show_on_front=page, page_for_posts={id}). On a posts page request, $wp_query contains both:
$wp_query->queried_object — the page itself (WP_Post)$wp_query->posts — the post type’s posts (WP_Post[])This plugin replicates this exact behavior for custom post types, with no extra queries or new functions needed to get your page object.
Once activated, your public custom post types appear in Settings > Reading.
Select any published page to serve as the archive page for each custom post type.
An optional checkbox lets you use the assigned page’s slug as the post type’s rewrite slug. This means your archive URL and single post URLs will share the same base path (e.g., /products/ for the archive and /products/my-product/ for a single post).
Warning: Enabling this option changes all single post URLs for the post type. Consider the SEO implications before toggling it on an existing site.
Once “Use page slug as rewrite slug” is on, changing the page slug breaks every published URL for that custom post type. The plugin guards both editing surfaces:
Both only fire when the slug actually differs from the saved value, on pages assigned to a CPT with “use page slug” enabled.
| CPT archive | Page for CPT | |
|---|---|---|
| Conditionals | is_post_type_archive = trueis_archive = true |
is_home = trueis_{posttype}_page = trueis_page_for_custom_post_type = $posttype |
| Queried object | WP_Post_Type |
WP_Post |
| Template hierarchy | archive-{posttype}.phparchive.phpindex.php |
home-{posttype}.phphome.phpindex.php |
// Check if the current page is a "page for custom post type"
is_page_for_custom_post_type(?string $postType = null): bool
// Get the custom post type associated with a page ID
get_custom_post_type_for_page(int $pageId): ?string
// Get the page ID assigned to a custom post type
get_page_id_for_custom_post_type(?string $postType = null): ?int
// Get the URL for a custom post type's archive page
get_page_url_for_custom_post_type(?string $postType = null): ?string
All functions are available both in the n5s\PageForCustomPostType namespace and in the global namespace.
// The post type slug, or false if not a PFCPT page
$wp_query->is_page_for_custom_post_type
// Boolean for a specific post type (e.g., is_product_page)
$wp_query->is_{posttype}_page
| Filter | Description |
|---|---|
pfcpt/page_ids |
Modify the array of page ID / post type mappings |
pfcpt/post_type_from_id/page_id |
Filter page ID resolution for a post type |
pfcpt/dropdown_page_args |
Customize the page dropdown arguments in Settings |
| Action | Description |
|---|---|
pfcpt/template_redirect |
Fires on template_redirect when on a PFCPT page |
pfcpt/flush_rewrite_rules |
Fires before rewrite rules are flushed |
Full multilingual support:
Requires Polylang 3.4+.
CollectionPage schema markupRequires Yoast SEO 26+.
Multilingual support via WPML’s String Translation:
Requires WPML 4.5+.
Requires The SEO Framework 5.1+.
is_page_for_custom_post_type location ruleRequires ACF 6+.
composer require n5s/page-for-custom-post-type
Download the latest page-for-custom-post-type-X.Y.Z.zip from the releases page and upload it via Plugins > Add New > Upload Plugin. The zip ships with vendor dependencies bundled, so no build step is required.
1.0 makes the “use page slug” behavior opt-in (it was previously always on). On upgrade, the plugin automatically enables it for every CPT that already has a page assigned, so existing URLs stay intact. If you’d rather use the default CPT rewrite slugs going forward, uncheck the option per CPT under Settings > Reading.
The legacy procedural API (is_page_for_custom_post_type(), get_page_id_for_custom_post_type(), etc.) is preserved as deprecated shims that forward to the namespaced equivalents. They’ll emit a _doing_it_wrong notice when WP_DEBUG is on.
GPL-3.0-or-later