WordPress Metaboxes

As web developer I use sometimes WordPress as framework, usually I have to setup custom post types to accomplish requirements. This is useful to non-developers create content separating common posts from portfolio, team, books or anything you could imagine.

So, what are metaboxes?

Metaboxes are handy, flexible, modular edit screen elements that can be used to collect information related to the post being edited. Then you can get that information in your template using get_post_meta function.

Unfortunately this is quite hard first time you try to implement, that's why I will explain process easier than WordPress Docs.

Setting up post type

First step is configure our own post_type where we will add some metaboxes. As many times we need to add more than 1 post type, I like to push them in an array and then loop with register_post_type().

In this example I'm going to setup a book post type with labels, archives, taxonomies and custom slug. This code must be in your functions.php file of your template.

add_action( 'init', 'create_post_types' );
function create_post_types() {
    $post_types = [];

    $post_types['book'] = array(
        'labels' => array(
          'name' => __( 'Books', 'child-theme' ),
          'singular_name' => __( 'Book', 'child-theme' ),
          'add_new' => __('Add new', 'child-theme'),
          'add_new_item' => __('Add new Book', 'child-theme' ),
          'edit_item' => __('Edit Book', 'child-theme' ),
          'new_item' => __('New Book', 'child-theme' ),
          'view_item' => __('View Book', 'child-theme' ),
          'view_items' => __('View Books', 'child-theme'),
          'search_items' => __('Search Book', 'child-theme'),
          'not_found' => __('No Books found', 'child-theme'),
          'not_found_in_trash' => __('No Books found in Trash', 'child-theme'),
          'all_items' => __('All Books', 'child-theme'),
          'archives' => __('Book Archives', 'child-theme'),
          'attributes' => __('Book Attributes', 'child-theme'),
        'public'             => true,
        'description'        => __('Books written by author', 'child-theme'),
        'publicly_queryable' => true,
        'show_ui'            => true,
        'show_in_menu'       => true,
        'query_var'          => true,
        'rewrite'            => array( 'slug' => 'book' ),
        'has_archive'        => true,
        'hierarchical'       => true,
        'menu_position'      => 5,  // Choose menu order (5 after Posts)
        'menu_icon'          => 'dashicons-book',
        'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'comments'),
        'taxonomies'         => array( 'category', 'tags' ),
        'capability_type'    => 'post',
        'map_meta_cap'       => true

    foreach($post_types as $name => $args ) {
        register_post_type( $name, $args );

Main options description:

  • labels: Add customized text for post type name and dashboard actions/fields
  • rewrite: Setup your own slug for pages and archives
  • menu_icon: Lets you to customize dashboard icon from WordPress Dashicons
  • supports: WordPress supports some default fields to support such as editor, thumbnail, comments or excerpt. More info
  • capability_type: You may pass an array to construct capabilities then e.g. array('book', 'books'), or simply you can type "post" to abstract default permissions for this post_type.

Books Post Type

Template files

Archives and single page views could be displayed following WordPress templating structure.

  • Single posts will use single-{post_type}.php
  • Archives will use archive-{post_type}.php

Ok, now our custom post_type is created and we're going to add a metabox on it.

Metaboxes Fields

We must use add_meta_box() inside our functions.php:

add_meta_box( $id, $title, $callback, $screen, $context, $priority, $callback_args)
  • id used in the 'id' form attribute. No matter what you type here, just must be unique
  • title of the Metabox admin fields
  • callback is a function which should echo the form output
  • screen string or array about where show metabox, in this case is our post type
  • side where the boxes should display in post edit ('normal', 'side' or 'advanced')
  • priority within the context where boxes should show ('high', 'low', 'default')
  • callback_args is data given as second parameter to your callback (we don't need in this example)

No need to use an action, but I recommend it to centralize and also may you have to add many metaboxes in different post types.

add_action( 'add_meta_boxes', 'all_post_metaboxes' );  
function all_post_metaboxes()
    add_meta_box( 'book-additional-data', __('Book Data', 'child-theme'), 'book_data_meta_box', 'book', 'normal', 'high' );

function book_data_meta_box( $post ) {
    $values = get_post_meta( $post->ID, null, true );
    $author = esc_attr( get_post_meta( $post->ID, 'book_author', true ) );
    $featured = esc_attr( get_post_meta( $post->ID, 'book_featured', true ) );

    wp_nonce_field( 'book_additional_data_nonce', 'book_additional_data_nonce' );
        <label for="book_author">Author</label>
        <input type="text" name="book_author" id="book_author" value="<?php echo $author; ?>"/>
        <input type="checkbox" id="book_featured" name="book_featured" <?php checked( $featured, 'on' ); ?> />
        <label for="book_featured">Featured</label>

Book Metabox

Metabox saving

We have ready our form to the custom post type, now it's the time to save the data.

add_action( 'save_post', 'book_meta_box_save' );
function book_meta_box_save( $post_id )
    // Bail if we're doing an auto save  
    if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;

    // if our nonce isn't there, or we can't verify it, bail
    if( !isset( $_POST['book_additional_data_nonce'] ) || !wp_verify_nonce( $_POST['book_additional_data_nonce'], 'book_additional_data_nonce' ) ) return;

    // if current user can't edit this post, bail  
    if( !current_user_can( 'edit_post' ) ) return;

    // Make sure your data is set before trying to save it  
    if( isset( $_POST['book_author'] ) )
        update_post_meta( $post_id, 'book_author', wp_kses($_POST['book_author'], $allowed ) );

    // This is purely my personal preference for saving check-boxes
    $featured = isset( $_POST['book_featured'] ) && $_POST['book_featured'] ? 'on' : 'off';
    update_post_meta( $post_id, 'book_featured', $featured );

This function must check nonce is correct for security reasons and we must ensure current user is able to edit posts. Once checking are passed, we can save data using update_post_meta().

And that's all! You can play with your knowledge to setup different fields like textareas, select, radio, or even hidden input.

Just remember to cleanup user submissions to avoid code injections... Also when just you are gonna use the site!

Leave a comment if it was helpful