How to Make Your Own Custom Post Formats in WordPress

Post Formats were introduced into WordPress awhile back to mimic those available in Tumblr, and theoretically across other blogging platforms. The first thing any good developer wanted to know was, “How do I make my own?”

Well, you can’t. This is by design, because they’re supposed to be “industry standard” formats that allow them to be ported from one platform to another. Swell.

custom post formats
You can’t add new Post Formats to WordPress’ built in feature, however you can just create your very own setup instead via custom taxonomies and some other trickery.

What if you want to create your own system of post formats though, such as I needed for a recent project? NBD, we can do it fairly easily as Post Formats are just another type of custom taxonomy. All we need to do is:

  1. Create a custom taxonomy and assign it to posts.
  2. Programmatically create the terms in that custom taxonomy, in a way where non-admins can’t create. edit or delete new ones, but can assign them to posts.
  3. Set a default post term.
  4. Change the way the custom taxonomy is displayed on the post editor page, so that only one format can be chosen.

Depending on your needs, of course, steps 2, 3 and 4 may be optional, but if you’re not restricting your authors to a specific set of formats, you might as well just create the custom taxonomy and be done with it. This setup is useful if you want to have custom post formats so that you can style certain posts differently or something.

Here’s the complete code, just add it to your functions.php and voila!

// hook into the init action and call custom_post_formats_taxonomies when it fires
add_action( 'init', 'custom_post_formats_taxonomies', 0 );

// create a new taxonomy we're calling 'format'
function custom_post_formats_taxonomies() {
// Add new taxonomy, make it hierarchical (like categories)
$labels = array(
'name' => _x( 'Formats', 'taxonomy general name', 'textdomain' ),
'singular_name' => _x( 'Format', 'taxonomy singular name', 'textdomain' ),
'search_items' => __( 'Search Formats', 'textdomain' ),
'all_items' => __( 'All Formats', 'textdomain' ),
'parent_item' => __( 'Parent Format', 'textdomain' ),
'parent_item_colon' => __( 'Parent Format:', 'textdomain' ),
'edit_item' => __( 'Edit Format', 'textdomain' ),
'update_item' => __( 'Update Format', 'textdomain' ),
'add_new_item' => __( 'Add New Format', 'textdomain' ),
'new_item_name' => __( 'New Format Name', 'textdomain' ),
'menu_name' => __( 'Format', 'textdomain' ),

$args = array(
'hierarchical' => true,
'labels' => $labels,
'show_ui' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'format' ),
'capabilities' => array(
'manage_terms' => '',
'edit_terms' => '',
'delete_terms' => '',
'assign_terms' => 'edit_posts'
'public' => true,
'show_in_nav_menus' => false,
'show_tagcloud' => false,
register_taxonomy( 'format', array( 'post' ), $args ); // our new 'format' taxonomy

// programmatically create a few format terms
function example_insert_default_format() { // later we'll define this as our default, so all posts have to have at least one format
'description' => '',
'slug' => 'default'
add_action( 'init', 'example_insert_default_format' );

// repeat the following 11 lines for each format you want
function example_insert_map_format() {
'Map', // change this to
'description' => 'Adds a large map to the top of your post.',
'slug' => 'map'
add_action( 'init', 'example_insert_map_format' );

// make sure there's a default Format type and that it's chosen if they didn't choose one
function moseyhome_default_format_term( $post_id, $post ) {
if ( 'publish' === $post->post_status ) {
$defaults = array(
'format' => 'default' // change 'default' to whatever term slug you created above that you want to be the default
$taxonomies = get_object_taxonomies( $post->post_type );
foreach ( (array) $taxonomies as $taxonomy ) {
$terms = wp_get_post_terms( $post_id, $taxonomy );
if ( empty( $terms ) && array_key_exists( $taxonomy, $defaults ) ) {
wp_set_object_terms( $post_id, $defaults[$taxonomy], $taxonomy );
add_action( 'save_post', 'moseyhome_default_format_term', 100, 2 );

// replace checkboxes for the format taxonomy with radio buttons and a custom meta box
function wpse_139269_term_radio_checklist( $args ) {
if ( ! empty( $args['taxonomy'] ) && $args['taxonomy'] === 'format' ) {
if ( empty( $args['walker'] ) || is_a( $args['walker'], 'Walker' ) ) { // Don't override 3rd party walkers.
if ( ! class_exists( 'WPSE_139269_Walker_Category_Radio_Checklist' ) ) {
class WPSE_139269_Walker_Category_Radio_Checklist extends Walker_Category_Checklist {
function walk( $elements, $max_depth, $args = array() ) {
$output = parent::walk( $elements, $max_depth, $args );
$output = str_replace(
array( 'type="checkbox"', "type='checkbox'" ),
array( 'type="radio"', "type='radio'" ),
return $output;
$args['walker'] = new WPSE_139269_Walker_Category_Radio_Checklist;
return $args;

add_filter( 'wp_terms_checklist_args', 'wpse_139269_term_radio_checklist' );

Muchas gracias to the many resources I found that helped put this all together, i.e. TheDeadMedic on WordPress StackExchange for how to get the radio buttons instead of checkboxes, mayeenulislam on GitHub for the code to set a default term in our taxonomy, and Tom McFarlin for how to programmatically set terms in a custom taxonomy. Oh, and of course, my family, Zeus, and everyone at the Screen Writer’s Guild for never giving up on me when I was just a burgeoning actor living in the mean streets of South Nanty Glo…or something.

Up Next: WTF? Insecure Content Warnings with WordPress Sites & WP Engine Makes No Sense!