# Extended CPTs: WordPress post types with admin columns and meta filters built in

> Extended CPTs is a GPL-2.0 licensed PHP library that wraps register_post_type and register_taxonomy with generated labels, admin table columns, list table filters, meta box controls and permalink structures, so a custom post type takes a few lines instead of an argument array. It is a Composer dependency, not a plugin, and the repository carries a maintainer-wanted topic.

**johnbillion/extended-cpts** — A library which provides extended functionality to WordPress custom post types and taxonomies.

- Repository: https://github.com/johnbillion/extended-cpts
- Stars: 1,028 · Forks: 92
- Language: PHP
- License: GPL-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/johnbillion-extended-cpts

## A library, not a plugin, and that changes the install

Extended CPTs is described as a library which provides extended functionality to WordPress custom post types and taxonomies, allowing developers to quickly build post types and taxonomies without having to write the same code again and again. It is by John Blackbourn.

The installation section draws the distinction that decides how you use it. Extended CPTs is a developer library, not a plugin, which means you need to include it as a dependency in your project, and the method is Composer:

```bash
composer require johnbillion/extended-cpts
```

The documentation also says that other means of installation or usage, particularly bundling within a plugin, is not officially supported and done at your own risk. That is a specific and unusual thing to say, and it is worth taking seriously rather than skimming past. Bundling the library into a plugin would mean shipping someone else's GPL code inside your own artefact, and the maintainer is declining to support that path rather than bless it.

So the mental model is: you are building a plugin or a theme, you add this to your composer.json, and you call its registration functions from your own plugin bootstrap.

The compatibility targets are stated as minimum requirements. PHP 7.4 is the floor and it is tested up to PHP 8.4. WordPress 6.0 is the floor and it is tested up to WP 6.8. The badges confirm PHP 7 and 8 support and the licence is GPLv2 or later, with the licence file at the repository root.

The repository itself is a normal PHP library layout: extended-cpts.php and functions.php at the root, src/ for the implementation, tests/ for the suite, composer.json, and a phpcs.xml.dist and phpstan.neon.dist for coding standards and static analysis.

## One parameter is enough to get a working post type

The demonstration of what the library saves starts with the minimal case. A post type with no frills is registered with a single parameter:

```php
add_action( 'init', function() {
	register_extended_post_type( 'article' );
} );
```

And a taxonomy takes two:

```php
add_action( 'init', function() {
	register_extended_taxonomy( 'location', 'article' );
} );
```

The result, as the documentation puts it, is a hierarchical public post type with an admin UI, a hierarchical public taxonomy with an admin UI, and all the labels and updated messages for them automatically generated.

The defaults behind that are listed explicitly for post types: automatically generated labels and post updated messages in English, a public post type with admin UI and post thumbnails enabled, hierarchical with the page capability type, and optimal admin menu placement. For taxonomies the defaults are automatically generated labels and term updated messages, and a hierarchical public taxonomy with admin UI enabled.

The capability type detail is the one with teeth. Registering a post type with a custom capability type means a custom role set, and getting that wrong is a classic source of plugins where editors cannot edit their own content. Making it hierarchical with the page capability type by default sidesteps that entirely for the common case.

The messages being in English is worth noting too. The generated labels and updated messages are English, so a site in another language gets an English admin until you override them.

## Admin columns, sortable meta columns and list table filters

This is where the library earns its keep, and it is the area where the argument array approach to register_post_type becomes unmaintainable.

On the post type listing screen, columns are created declaratively. The supported column types are post meta, taxonomy terms, featured images, post fields, Posts 2 Posts connections, and custom functions. Some of them are sortable, specifically post meta, taxonomy terms and post fields, and there is support for user capability restrictions on a column and for a default sort column and sort order.

On the taxonomy term listing screen, the same idea applies with columns for term meta and custom functions, plus user capability restrictions.

Then there are filter controls on the post type listing screen, which let you filter posts by post meta, taxonomy terms, post author and post dates. WordPress core gives you none of that out of the box, so a post type with a meta field you need to filter on means writing a custom filter, a query modification and a form by hand.

The example shows what that looks like in configuration:

```php
'admin_cols' => [
	'story_featured_image' => [
		'title'          => 'Illustration',
		'featured_image' => 'thumbnail'
	],
	'story_published' => [
		'title_icon'  => 'dashicons-calendar-alt',
		'meta_key'    => 'published_date',
		'date_format' => 'd/m/Y'
	],
	'story_genre' => [
		'taxonomy' => 'genre'
	],
],
'admin_filters' => [
	'story_genre' => [
		'taxonomy' => 'genre'
	],
	'story_rating' => [
		'meta_key' => 'star_rating',
	],
],
```

The key naming, story_featured_image and story_published, is a convention where the post type prefix keeps your column keys distinct from other plugins' column keys in the same global column registry. The taxonomy and meta_key keys are what tell the library where the data comes from.

There are also smaller admin features: overriding the Featured Image and Enter title here text, several custom meta box styles for taxonomies on the post editing screen with a simplified list of checkboxes, radio buttons, a dropdown menu and a custom function option, and automatic addition of post types and taxonomies to the At a Glance section of the dashboard, with post types optionally added to the Recently Published section.

## Front-end control over permalinks and query vars

The front-end features are the part of the library that reaches past the admin screen and into how the post type behaves on the public site.

A custom permalink structure can be specified, with the example given as reviews/%year%/%month%/%review%. It supports all relevant rewrite tags including dates and custom taxonomies, and it integrates automatically with the Rewrite Rule Testing plugin, which is a nice touch, because a broken rewrite rule is otherwise hard to diagnose by hand.

The query var features are the more substantial part. You can specify public query vars which enable filtering by post meta and post dates, and you can specify public query vars which enable sorting by post meta, taxonomy terms and post fields. And you can override default public or private query vars such as posts_per_page, orderby, order and nopaging.

That last group matters more than it looks. WordPress has a set of private query vars that control the main query behaviour, and the archive example in the documentation uses one of them:

```php
'archive' => [
	'nopaging' => true,
],
```

Showing all posts on the post type archive, with no pagination, is a one-line change here. Doing that with core alone means hooking pre_get_posts and fighting the default query.

The remaining front-end feature is adding the post type to the site's main RSS feed, which is the show_in_feed option in the example. The set of post type and taxonomy base names can also be overridden, for example singular Story, plural Stories and slug stories, so the generated labels match what you actually call them.

## What the library is actually a wrapper for

The documentation states the relationship plainly: the register_extended_post_type() and register_extended_taxonomy() functions are ultimately wrappers for the register_post_type() and register_taxonomy() functions in WordPress core, so any of the parameters from those functions can be used.

That single sentence removes most of the anxiety about the library. It is not a parallel implementation of the post type API with its own semantics, it is the core function plus a layer that fills in sensible defaults and adds a handful of extra keys. Every core argument still works, and the extra keys are additions rather than replacements.

It also sets the right expectation about the risk. If your extended registration breaks, the core registration is what WordPress actually stores, and the failure is a matter of an argument value rather than a divergence from how WordPress works.

The parameters in the extended call are passed in two arrays in the fuller example, one for the features and one for the base names. The base names array is the one that is not a core concept, since core handles labels through a labels array with a lot of keys, and the extended form reduces that to singular, plural and slug.

The two functions are the entire public API of the library as far as the documentation is concerned, and the wiki carries the full reference. The documentation also points returning users at a Recent Changes for Developers page, which is a good sign for a library with a long history.

## The maintainer-wanted topic and what the test setup tells you

Two things in the repository metadata deserve a mention, and neither is in the README.

The first is the topic list, which contains maintainer-wanted alongside wordpress. That is a label maintainers apply when a project needs a new owner, and it tells you something real about the trajectory of this library. The release history supports that reading: the most recent tagged version is 5.1.0 from 2025-06-18, before that 5.0.12 from 2025-04-16 and 5.0.11 from 2024-11-30. The last push was on 2026-09-14, so commits are still happening, but the release cadence has slowed and the project is looking for someone to carry it.

That does not make it a bad dependency, but it does change what you should ask. If you adopt a library whose maintainer is looking for a successor, you are signing up for a potential fork, and the version numbers in your composer.json stop being a promise about future compatibility. The library is stable, widely used in the WordPress ecosystem, and its API is a thin wrapper over core, so a fork would not be disruptive. But it is a factor for a long-lived project.

The second is the test setup. The repository has codeception.dist.yml, a docker-compose.yml and a tests/ directory, and the docker-compose file includes a compose file from a dependency:

```yaml
include:
  -
    path: vendor/johnbillion/plugin-infrastructure/tests/docker-compose.yml
    project_directory: .
```

So the test suite runs against a WordPress instance provided by a shared infrastructure package, pulled in through Composer, and the include with a project_directory override tells Compose to resolve relative paths against the repository root. The GitHub Actions badge in the readme refers to an integration-tests workflow on the develop branch, which is also the default branch.

Testing a WordPress library properly means running against a real WordPress install, and the Codeception configuration plus the containerised environment is how that is done here. The combination of a shared test infrastructure package and a maintainer-wanted topic says something about the scale of the effort involved in keeping this working.

## Conclusion

Extended CPTs is a good pick for a WordPress plugin or theme that registers several custom post types and has to live with the admin interface afterwards, because the admin columns, sortable meta columns and list table filters are the parts of register_post_type that everyone writes badly and nobody wants to write again, and the GPL-2.0 terms suit WordPress distribution; it is the wrong pick if you need a plugin you can activate from the admin screen, since it is a library with no plugin UI, and before you adopt it you should check the supported matrix, because the documented floor is PHP 7.4 and WordPress 6.0 with testing up to PHP 8.4 and WordPress 6.8.

## FAQ

### How do I install Extended CPTs?

It is a developer library rather than a plugin, so you add it as a Composer dependency with composer require johnbillion/extended-cpts. The documentation states that other installation methods, particularly bundling within a plugin, are not officially supported and are done at your own risk.

### What does register_extended_post_type do?

It wraps the core register_post_type function, filling in improved defaults such as generated labels and updated messages, a public post type with admin UI and thumbnails, hierarchical with the page capability type, and optimal admin menu placement, while adding admin columns, list table filters and front-end permalink and query var options.

### What are the minimum requirements for Extended CPTs?

PHP 7.4, tested up to PHP 8.4, and WordPress 6.0, tested up to WP 6.8. The licence is GPLv2 or later.

### Can I add sortable columns and filters to a post type listing screen?

Yes. The admin_cols configuration creates columns on the post type listing screen for post meta, taxonomy terms, featured images, post fields, Posts 2 Posts connections and custom functions, with post meta, taxonomy terms and post fields sortable, and admin_filters adds dropdown filters for post meta, taxonomy terms, post author and post dates.

### Is Extended CPTs still maintained?

Commits are still landing, with the last push on 2026-09-14, and the most recent release is 5.1.0 from 2025-06-18. The repository carries a maintainer-wanted topic, which indicates the project is looking for someone to take it on.

## Sources

- [Issues](https://github.com/johnbillion/extended-cpts/issues)
- [johnbillion/extended-cpts on GitHub](https://github.com/johnbillion/extended-cpts)
- [License: GPL-2.0](https://github.com/johnbillion/extended-cpts/blob/develop/LICENSE)
- [README](https://github.com/johnbillion/extended-cpts/blob/develop/README.md)
- [Releases](https://github.com/johnbillion/extended-cpts/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/johnbillion-extended-cpts
