PHPackages                             n5s/page-for-custom-post-type - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. n5s/page-for-custom-post-type

ActiveWordpress-plugin[Utility &amp; Helpers](/categories/utility)

n5s/page-for-custom-post-type
=============================

Page for custom post types, just like page for posts

1.1.0(1mo ago)98313[1 issues](https://github.com/nlemoine/page-for-custom-post-type/issues)[1 PRs](https://github.com/nlemoine/page-for-custom-post-type/pulls)GPL-3.0-or-laterPHPPHP ^8.2

Since Jun 16Pushed 1mo ago3 watchersCompare

[ Source](https://github.com/nlemoine/page-for-custom-post-type)[ Packagist](https://packagist.org/packages/n5s/page-for-custom-post-type)[ Docs](https://github.com/nlemoine/page-for-custom-post-type)[ RSS](/packages/n5s-page-for-custom-post-type/feed)WikiDiscussions main Synced today

READMEChangelog (3)Dependencies (40)Versions (8)Used By (0)

Page for Custom Post Type
=========================

[](#page-for-custom-post-type)

[![QA](https://camo.githubusercontent.com/6c28a991a91da9b11ed9d24fdd82b442963449fee2cf4468c6b8c48e8a944740/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6e6c656d6f696e652f706167652d666f722d637573746f6d2d706f73742d747970652f71612e796d6c3f6272616e63683d6d61696e266c6162656c3d7161)](https://github.com/nlemoine/page-for-custom-post-type/actions/workflows/qa.yml)[![Coverage](https://camo.githubusercontent.com/0f4d173283910622813c58f4b9a7fc2f1de0b75f92f4d8e0efe383c13f152246/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f6e6c656d6f696e652f706167652d666f722d637573746f6d2d706f73742d74797065)](https://codecov.io/gh/nlemoine/page-for-custom-post-type)[![PHPStan](https://camo.githubusercontent.com/ff3c7f8c8667ce643f47e74532748f673482a5f95d7d4269f925f2eebbe5117e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230382d627269676874677265656e)](https://phpstan.org/)[![Packagist Downloads](https://camo.githubusercontent.com/ef79c17386d4fb6fe37874424c4b282bcbd77996b85296df6d0562e9a9f36735/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6e35732f706167652d666f722d637573746f6d2d706f73742d74797065)](https://packagist.org/packages/n5s/page-for-custom-post-type)

Assign any WordPress page as the archive page for a custom post type — just like the native "page for posts" setting.

The problem
-----------

[](#the-problem)

WordPress custom post type archives are dynamically generated and can't be edited as regular pages. This creates recurring issues:

- **No editable content**: clients can't add a title, excerpt, cover image, or custom fields to archive pages.
- **No SEO control**: archive pages lack the metadata management that pages offer through SEO plugins.
- **No page builder support**: archive templates can't leverage the page editor or custom fields.

Several plugins have attempted to solve this:

- [post-type-archive-pages](https://github.com/highrisedigital/post-type-archive-pages)
- [wp-post-type-archive-pages](https://github.com/DarrenTheDev/wp-post-type-archive-pages)
- [page-for-post-type (humanmade)](https://github.com/humanmade/page-for-post-type)
- [page-for-post-type (statenweb)](https://github.com/statenweb/page-for-post-type)

While these provided inspiration, none fully replicate WordPress's native behavior.

Approach
--------

[](#approach)

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.

Setup
-----

[](#setup)

Once activated, your public custom post types appear in **Settings &gt; Reading**.

[![Settings > Reading](https://private-user-images.githubusercontent.com/2526939/246384955-c725b560-ef7c-468e-9607-ef1617154c1c.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODI3MzY4ODEsIm5iZiI6MTc4MjczNjU4MSwicGF0aCI6Ii8yNTI2OTM5LzI0NjM4NDk1NS1jNzI1YjU2MC1lZjdjLTQ2OGUtOTYwNy1lZjE2MTcxNTRjMWMucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI2MDYyOSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNjA2MjlUMTIzNjIxWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NjZkNzBlYmUyMjgwZmE4ZDQ3MzE1YjUxYjA2ZWU5OGIxYzRkYjgxYzE3NjM4MWQyN2EyYmM4M2M4ZTg1MjYyZCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmcmVzcG9uc2UtY29udGVudC10eXBlPWltYWdlJTJGcG5nIn0.FuSzk0eNfSF0pup1gcQjC9wnAP-P2uwkb6sygZW_elk)](https://private-user-images.githubusercontent.com/2526939/246384955-c725b560-ef7c-468e-9607-ef1617154c1c.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODI3MzY4ODEsIm5iZiI6MTc4MjczNjU4MSwicGF0aCI6Ii8yNTI2OTM5LzI0NjM4NDk1NS1jNzI1YjU2MC1lZjdjLTQ2OGUtOTYwNy1lZjE2MTcxNTRjMWMucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI2MDYyOSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNjA2MjlUMTIzNjIxWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NjZkNzBlYmUyMjgwZmE4ZDQ3MzE1YjUxYjA2ZWU5OGIxYzRkYjgxYzE3NjM4MWQyN2EyYmM4M2M4ZTg1MjYyZCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmcmVzcG9uc2UtY29udGVudC10eXBlPWltYWdlJTJGcG5nIn0.FuSzk0eNfSF0pup1gcQjC9wnAP-P2uwkb6sygZW_elk)

Select any published page to serve as the archive page for each custom post type.

### Use page slug as rewrite slug

[](#use-page-slug-as-rewrite-slug)

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.

### Slug-change protection

[](#slug-change-protection)

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:

- **Block editor**: a warning notice and confirmation checkbox appear in the document sidebar; the Update button stays disabled until the checkbox is ticked.
- **Quick Edit** on the Pages list: a confirmation dialog blocks the save until acknowledged.

Both only fire when the slug actually differs from the saved value, on pages assigned to a CPT with "use page slug" enabled.

Key differences with native CPT archives
----------------------------------------

[](#key-differences-with-native-cpt-archives)

CPT archivePage for CPTConditionals`is_post_type_archive` = `true`
`is_archive` = `true``is_home` = `true`
`is_{posttype}_page` = `true`
`is_page_for_custom_post_type` = `$posttype`Queried object`WP_Post_Type``WP_Post`Template hierarchy`archive-{posttype}.php`
`archive.php`
`index.php``home-{posttype}.php`
`home.php`
`index.php`API
---

[](#api)

### Functions

[](#functions)

```
// 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.

### Query properties

[](#query-properties)

```
// 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
```

### Hooks

[](#hooks)

#### Filters

[](#filters)

FilterDescription`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#### Actions

[](#actions)

ActionDescription`pfcpt/template_redirect`Fires on `template_redirect` when on a PFCPT page`pfcpt/flush_rewrite_rules`Fires before rewrite rules are flushedIntegrations
------------

[](#integrations)

### Polylang

[](#polylang)

Full multilingual support:

- Each language can have its own assigned page
- Archive URLs are automatically translated
- Page slugs are translated when using the "use page slug" option
- Settings dropdown only shows pages in the default language

Requires Polylang 3.4+.

### Yoast SEO (WordPress SEO)

[](#yoast-seo-wordpress-seo)

- Full SEO metadata support on archive pages
- Correct breadcrumb trails (archive page appears in single post and taxonomy breadcrumbs)
- Proper `CollectionPage` schema markup
- Pages are indexed as pages, not archives

Requires Yoast SEO 26+.

### WPML

[](#wpml)

Multilingual support via WPML's String Translation:

- Each language can have its own assigned page
- Archive URLs follow WPML's URL structure
- Page slugs are translatable when using the "use page slug" option

Requires WPML 4.5+.

### The SEO Framework

[](#the-seo-framework)

- SEO metadata support
- Correct breadcrumb trails
- Proper query type detection (page, not archive)

Requires The SEO Framework 5.1+.

### Advanced Custom Fields

[](#advanced-custom-fields)

- Adds a `is_page_for_custom_post_type` location rule
- Allows field groups to be conditionally displayed on PFCPT pages

Requires ACF 6+.

Requirements
------------

[](#requirements)

- WordPress 6.0+
- PHP 8.2+

Installation
------------

[](#installation)

### Composer (recommended)

[](#composer-recommended)

```
composer require n5s/page-for-custom-post-type
```

### Manual

[](#manual)

Download the latest `page-for-custom-post-type-X.Y.Z.zip` from the [releases page](https://github.com/nlemoine/page-for-custom-post-type/releases) and upload it via **Plugins &gt; Add New &gt; Upload Plugin**. The zip ships with vendor dependencies bundled, so no build step is required.

Upgrading from 0.x
------------------

[](#upgrading-from-0x)

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 &gt; 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.

License
-------

[](#license)

GPL-3.0-or-later

###  Health Score

51

—

FairBetter than 95% of packages

Maintenance91

Actively maintained with recent releases

Popularity26

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity62

Established project with proven stability

 Bus Factor1

Top contributor holds 92.2% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~8 days

Total

4

Last Release

31d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/62f16c9d375343e12eb02b2a3095dc9a02047287b5f12f75ef4c59929fcb2802?d=identicon)[n5s](/maintainers/n5s)

---

Top Contributors

[![nlemoine](https://avatars.githubusercontent.com/u/2526939?v=4)](https://github.com/nlemoine "nlemoine (95 commits)")[![huubl](https://avatars.githubusercontent.com/u/50170696?v=4)](https://github.com/huubl "huubl (4 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (3 commits)")[![losted](https://avatars.githubusercontent.com/u/165665?v=4)](https://github.com/losted "losted (1 commits)")

---

Tags

archive-pagecptcustom-post-typecustom-post-typesphpwordpresswordpress-developmentwordpress-pluginwordpresscustom-post-typecustom post type archivepage for custom post type

###  Code Quality

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/n5s-page-for-custom-post-type/health.svg)

```
[![Health](https://phpackages.com/badges/n5s-page-for-custom-post-type/health.svg)](https://phpackages.com/packages/n5s-page-for-custom-post-type)
```

###  Alternatives

[roots/bedrock

WordPress boilerplate with Composer, easier configuration, and an improved folder structure

6.5k463.8k2](/packages/roots-bedrock)[helsingborg-stad/municipio

A bootstrap theme for creating municipality sites.

4028.5k10](/packages/helsingborg-stad-municipio)[roots/wp-stage-switcher

WordPress plugin that allows you to switch between different environments from the admin bar

383469.2k3](/packages/roots-wp-stage-switcher)[vinkla/wordplate

The WordPlate boilerplate

2.2k5.3k](/packages/vinkla-wordplate)[mediawiki/maps

Adds various mapping features to MediaWiki

84152.3k3](/packages/mediawiki-maps)[civicrm/civicrm-drupal-8

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

19251.4k3](/packages/civicrm-civicrm-drupal-8)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
