How to add meta boxes in posts and pages

posted on by - 1062 views

In this tutorial we will learn how to add meta boxes in posts and pages without using plugins. Of course, the easiest way to do that is by using the awesome Advanced Custom Fields, which gives us an endless amount of options, but in some cases it may not be the best choice (maybe you’re developing a WordPress theme or plugin that you want to sell?).

So, let’s say we want to add the field Source in all our posts, so we can use it to write a link inside: how can we do that?

How to add custom meta boxes in WordPress

The first thing to do is going in our functions.php and create a function that will start building our meta boxes.

function theme_post_meta_boxes( $post ){
    // Adding meta boxes to posts
    add_meta_box( 'theme_meta_post_options', esc_html( "Theme: Post Options" ), 'theme_build_meta_post_options' );

As we can see, we use add_meta_box to tell our theme the ID of the meta boxes we want to create (in this case, theme_meta_post_options), the name we will see in our admin panel (Theme: Post Options), and another function we need to actually built them (theme_build_meta_post_options).

Now let’s go create that function.

function theme_build_meta_post_options( $post ){
    // Build meta options for posts
    wp_nonce_field( basename( __FILE__ ), 'theme_meta_nonce_post' );

    // Retrieve data
    $theme_current_post_source = get_post_meta( $post->ID, '_theme_current_post_source', true );

    // Print boxes ?>
    <div class="theme_post_meta_boxes">
        <div class="inside">
            <!-- source -->
                        <h4 class="theme_meta_option_title"><?php esc_html( "Source" );?></h4>
                        <input name="theme_current_post_source" type="url" value="<?php if( isset( $theme_current_post_source ) ){ echo esc_attr( $theme_current_post_source ); }?>" />
<?php }

Okay, that’s a lot of stuff. Let’s see what we did:

  • first, we set a nonce, which is a security measure that will be useful later, when we will save our values;
  • then, we retrieve the existing data, meaning that if there’s already a value saved, we store it in a variable, so that we can automatically fill the input field;
  • then we physically build the meta boxes. Just HTML code here (note that in the input value we check if the variable containing our value is set – if so, we put it as a value).

Good, now we have the physical structure of our meta boxes. We now need a function (actually two functions) to save their values.

function theme_save_post_meta_boxes( $post_id ){
    // Check permissions
    if( !current_user_can( 'edit_post', $post_id ) ){

    // Save post meta boxes
    theme_save_meta_post_options( $post_id );

This was the first one, which acts as a security measure. Our code will check if the user trying to save the meta boxes has the right permissions: if so, go on and call the other function, which we’re going to build now; if not, return and do nothing.

Now, let’s build the other function that will save the meta boxes values.

function theme_save_meta_post_options( $post_id ){
    // Check nonce
    if ( !isset( $_POST[ 'theme_meta_nonce_post' ] ) || !wp_verify_nonce( $_POST[ 'theme_meta_nonce_post' ], basename( __FILE__ ) ) ){

    // SOURCE
    if( isset( $_POST[ 'theme_current_post_source' ] ) ){
      $theme_current_post_source = esc_url_raw( $_POST[ 'theme_current_post_source' ] );
      update_post_meta( $post_id, '_theme_current_post_source', $theme_current_post_source );

Great. So this function:

  • first checks if all it’s okay with the nonce;
  • if so, checks the value of our meta box and updates the post meta.

Now, the only thing we have to do is to tell our code when to execute those functions. We can do that by adding actions.

// Add meta boxes for posts
add_action( 'add_meta_boxes_post', 'theme_post_meta_boxes' );

// Save meta boxes for posts
add_action( 'save_post_post', 'theme_save_post_meta_boxes', 10, 2 );

Note that we just used two hooks: add_meta_boxes_post and save_post_post. If we wanted to add meta boxes to pages or other custom post types, we would just have to change that last _post in our hooks. For example, in case of pages we would use add_meta_boxes_page and save_post_page.