Make WordPress Posts Hierarchical with a Parent Option

WordPress pages can have a parent page, posts cannot. Sometimes you want that for posts or a custom post type too: a guide split into chapters, documentation with sections, or a parent post that lists its child posts.

This snippet makes WordPress posts hierarchical. The editor gets the Parent and Order fields that pages have, the admin list shows children indented under their parent, and you can query child posts with post_parent.

The snippet

<?php
/**
 * Make WordPress posts (and other post types) hierarchical, with a Parent option in the editor.
 */

// ===== Config =====
function dpc_hierarchical_post_types() {
	// Post types that get a parent. Custom post types: add their slug, e.g. 'docs'.
	$post_types = array( 'post' );
	// ===== End of config =====

	return (array) apply_filters( 'dpc_hierarchical_post_types', $post_types );
}

add_filter( 'register_post_type_args', 'dpc_hierarchical_post_type_args', 10, 2 );

function dpc_hierarchical_post_type_args( $args, $post_type ) {
	if ( ! in_array( $post_type, dpc_hierarchical_post_types(), true ) ) {
		return $args;
	}

	$args['hierarchical'] = true;

	// No 'supports' given means WordPress's default: title and editor. false means none.
	$supports = $args['supports'] ?? array( 'title', 'editor' );
	if ( ! is_array( $supports ) ) {
		$supports = array();
	}
	if ( ! in_array( 'page-attributes', $supports, true ) ) {
		$supports[] = 'page-attributes';
	}
	$args['supports'] = $supports;

	return $args;
}

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, edit the config and publish. Leave Action/Hook empty.

Everywhere is needed: the filter must be in place before WordPress registers the post types on every request, in wp-admin, the REST API and the front end.

The same code also works unchanged in a small plugin or an mu-plugin.

How it works

  • register_post_type_args lets you change the arguments of any post type while it is registered, including core’s post and post types from other plugins.
  • hierarchical => true allows a post_parent. The page-attributes feature adds the Parent and Order fields to the editor’s sidebar.
  • A custom post type registered without supports gets WordPress’s defaults (title and editor) plus page-attributes. The older version used array_merge() on a missing supports value, which throws a TypeError on PHP 8 and stops the whole page.
  • The post type list sits in the config function, and other code can change it with the dpc_hierarchical_post_types filter.

Customise it

  • Custom post types: add their slug to $post_types, for example array( 'post', 'docs' ).
  • List child posts on the parent: use a Query Loop block, or get_children( array( 'post_parent' => get_the_ID(), 'post_type' => 'post' ) ) in PHP.

Gotchas

  • Post permalinks stay flat. A child post keeps /child-post/ (or your date-based structure), not /parent/child/ as pages do.
  • Hierarchical post types are listed differently in wp-admin: sorted by title and grouped under their parent, not by date. On a site with thousands of posts the list loads all post IDs to build the tree, which is slow.
  • Making post hierarchical is not an officially supported setup. Most themes and plugins handle it, but check RSS feeds, archives and SEO plugins after enabling it.
  • Removing the snippet keeps the post_parent values in the database; they are just no longer shown.

In the Scripts Organizer library Import it with one click from Scripts Organizer.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You’ll also get DPlugins product updates. No spam, and you can unsubscribe anytime.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You'll also get DPlugins product updates. No spam, and you can unsubscribe anytime.