In this WordPress tutorial, we will learn how to use the add_meta_box function to create custom advanced fields for posts and pages. These meta fields will store additional information needed to provide advanced features on our WordPress website.

In this guide, we’ll learn how to use custom meta fields to insert HTML <script> and <style> tags into the <head> section of our document. This feature allows us to add third-party JavaScript and CSS libraries to individual posts and pages.

Define a Function to Display Advanced Custom Fields

First, we’ll write HTML code to display an extra custom input box on the post and page screens. The function my_custom_acf will render this HTML code on our post and page screens.

// This function receives the current post object as a parameter
function my_custom_acf($post) {
    $html = '<div><label for="html-tags">' . __('HTML TAGS', 'theme-name') . '</label>';
    $html .= '<textarea id="html-tags" name="html-tags" rows="4" class="components-text-control__input"></textarea></div>';
    echo $html;
}

Adding a Meta Box

After defining the function to render HTML elements, we need to use the add_meta_box function to add a custom meta box to the post and page screens. The code below adds a meta box for collecting custom meta information related to posts or pages.

// This function receives an array of screens (e.g., post, page, attachment)
function register_custom_acf($post_type) {
    $my_screens = ['post', 'page'];
    if (in_array($post_type, $my_screens)) {
        add_meta_box(
            'my-custom-acf',
            __('Advanced Custom Field', 'theme-name'),
            'my_custom_acf',
            $post_type,
            'side'
        );
    }
}
// add_meta_box( string $id, string $title, callable $callback, string|array|WP_Screen $screen = null, string $context = ‘advanced’, string $priority = ‘default’, array $callback_args = null )

Parameters for the add_meta_box Function

  • Meta Box ID: Unique identifier for the meta box (used in the id attribute).
  • Title: The displayed title of the meta box.
  • Callback Function: The function that renders the HTML content (e.g., my_custom_acf).
  • Screen: The screen(s) where the box will appear, such as specific post types like post, page, link, or comment.
  • Context: The position within the screen where the box should display. Common contexts include:
    • Post edit screen: normal, side, and advanced.
    • Comments screen: normal and side.
    • Menus screen: side.
    • Default context is advanced.
  • Priority: Priority within the context (default, high, or low).
  • Callback Arguments: Data to set as the $args property of the box array (used as the second parameter in the callback function).

Registering Meta Boxes for Any Post Type

After adding our meta box, we need to register it to ensure it executes correctly. To register meta boxes, we use the add_meta_boxes hook, which fires after all built-in meta boxes have been added.

add_action('add_meta_boxes', 'register_custom_acf');

Now, if you refresh your post or page screen, you should see that our meta box is added successfully. However, it currently does not store any input in the database.

Saving Metadata to the Database with update_post_meta

To store input data in the database, we use the update_post_meta function. This function updates a meta field for a specific post based on its post ID. The following code saves our meta information related to the post in the database.

function save_my_meta_data($post_id) {
    // Return if this is an autosave
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    // Save input if it exists
    if (isset($_POST["html-tags"])) {
        $custom_meta['html_tags'] = trim($_POST["html-tags"]);
        update_post_meta($post_id, '_my_custom_meta', $custom_meta);
    }
}

To execute this code, attach the function to the save_post hook. This hook fires whenever a post is saved, making it ideal for our case.

add_action('save_post', 'save_my_meta_data');

Now, when you enter content in the custom meta field and save the post, the content will be saved in the database. However, if you refresh the page, you may notice that although the data is stored in the database, the meta field still appears empty.

Displaying Saved Metadata in the Meta Box

To resolve this, we need to update the my_custom_acf function so that it retrieves and displays saved metadata. Here’s the updated function:

function my_custom_acf($post) {
    $custom_meta = get_post_meta($post->ID, '_my_custom_meta', true);
    $mydata = isset($custom_meta['html_tags']) ? $custom_meta['html_tags'] : "";
    $html = '<div><label for="html-tags">' . __('HTML TAGS', 'theme-name') . '</label>';
    $html .= '<textarea id="html-tags" name="html-tags" rows="4" class="components-text-control__input">' . $mydata . '</textarea></div>';
    echo $html;
}

After refreshing the page, the custom meta field should now work perfectly, storing and retrieving post meta information as expected.

Using the wp_head Hook

Now, let’s implement the code to update the <head> section of our document with custom post meta information. For example, we can add a <link> tag to load third-party CSS libraries, like Bootstrap, on specific posts or pages.

function my_head_meta() {
    $custom_meta = get_post_meta(get_the_ID(), '_my_custom_meta', true);
    $mydata = isset($custom_meta['html_tags']) ? $custom_meta['html_tags'] : "";
    echo $mydata;
}
add_action( 'wp_head', 'my_head_meta' );

With this setup, you can use our custom meta field to insert any valid <head> tag into the HTML document of individual posts or pages. For example, you can paste the following <link> tag into our custom meta field to load Bootstrap CSS for a specific post:

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">

Complete Code to Create Advanced Custom Field

Here’s the complete code for creating advanced custom fields with the add_meta_box function:

<?php

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

function my_custom_acf($post) {
    $custom_meta = get_post_meta($post->ID, '_my_custom_meta', true);
    $mydata = isset($custom_meta['html_tags']) ? $custom_meta['html_tags'] : "";
    $html = '<div><label for="html-tags">' . __('HTML TAGS', 'theme-name') . '</label>';
    $html .= '<textarea id="html-tags" name="html-tags" rows="4" class="components-text-control__input">' . $mydata . '</textarea></div>';
    echo $html;
}

function register_custom_acf($post_type) {
    $my_screens = ['post', 'page'];
    if (in_array($post_type, $my_screens)) {
        add_meta_box( 'my-custom-acf', __('Advanced Custom Field', 'theme-name'), 'my_custom_acf', $post_type, 'side' );
    }
}
add_action('add_meta_boxes', 'register_custom_acf');

function save_my_meta_data($post_id) {
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }
    if (isset($_POST["html-tags"])) {
        $custom_meta['html_tags'] = trim($_POST["html-tags"]);
        update_post_meta($post_id, '_my_custom_meta', $custom_meta);
    }
}
add_action('save_post', 'save_my_meta_data');

function my_head_meta() {
    $custom_meta = get_post_meta(get_the_ID(), '_my_custom_meta', true);
    $mydata = isset($custom_meta['html_tags']) ? $custom_meta['html_tags'] : "";
    echo $mydata;
}

add_action( 'wp_head', 'my_head_meta' );

We can also use meta boxes to create settings pages for plugins and themes. For more information, read our article on creating a settings page for WordPress plugins and themes.