Adding custom fields to posts and pages in WordPress

Adding custom fields to posts and pages in WordPress

Online by JSC0d3
August 14, 2017 | | | | | 2086 recognitions

Do you want to capture more data when creating a post or a page? Do you want to add some custom fields to your custom post type? With WordPress’s metabox functionality, you can! There are a few moving parts here, so let’s walk through them one-by-one.

Create your metabox

We can use the add_meta_box() function to create new metaboxes. We need to hook into WordPress’s add_meta_boxes action hook.

In add_meta_box(), you’ll give your metabox an ID, a name, a function to use to generate the markup, a post type to display it on (posts, pages, etc.), and a location and priority on the page.

The post type can only be a single value (not an array), so if you want to use your metabox on multiple post types, you’ll need to repeat the add_meta_box() function a couple of times.

if ( ! function_exists( '_js_code_create_metabox' ) ) {
	 * Create the metabox
	 * @link
	function _js_code_create_metabox() {

		// Can only be used on a single post type (ie. page or post or a custom post type).
		// Must be repeated for each post type you want the metabox to appear on.
			'_js_code_id_post_metabox', // Metabox ID
			'New Meta post box', // Title to display
			'_js_code_render_metabox', // Function to call that contains the metabox content
			'post', // Post type to display metabox on
			'normal', // Where to put it (normal = main colum, side = sidebar, etc.)
			'default' // Priority relative to other metaboxes

		// To add the metabox to a page, too, you'd repeat it, changing the location
			'_js_code_id_page_metabox', // Metabox ID
			'New Meta page box', // Title to display
			'_js_code_render_metabox', // Function to call that contains the metabox content
			'page', // Post type to display metabox on
			'normal', // Where to put it (normal = main colum, side = sidebar, etc.)
			'default' // Priority relative to other metaboxes
add_action( 'add_meta_boxes', '_js_code_create_metabox' );

Rendering the metabox markup

Now we need to create content for the function specified in add_meta_box().

We’ll want to grab the $post, and get our saved field value (if one exists). We also need to include a security field that we’ll check against to make sure this is a valid submission from the WordPress Dashboard.

You’ll note that our field data is wrapped in an esc_attr() function. This escapes and encodes any data for proper use in an input field value.

if ( ! function_exists( '_js_code_render_metabox' ) ) {
	 * Render the metabox markup
	 * This is the function called in `_namespace_create_metabox()`
	function _js_code_render_metabox() {
		// Variables
		global $post; // Get the current post data
		$details = get_post_meta( $post->ID, '_js_code', true ); // Get the saved values

                <label for="_js_code_metabox">
					// This runs the text through a translation and echoes it (for internationalization)
					_e( 'Item Name', 'textdomain' );
				// The `esc_attr()` function here escapes the data for
				// HTML attribute use to avoid unexpected issues
                        value="<?php echo esc_attr( $details ); ?>"

		// Security field
		// This validates that submission came from the
		// actual dashboard and not the front end or
		// a remote server.
		wp_nonce_field( '_js_code_form_metabox_nonce', '_js_code_form_metabox_process' );


Saving your metabox

When the post or page is submitted, we want to save any data in our custom fields.

To do that, we first look to see if the submitted data contains our security field. If it does, we validate that field using the wp_verify_nonce() function. We also check that the submitting user has permission to edit the post.

Finally, we make sure that our field was submitted with data. If all criteria are met, we can save our field.

It’s important to sanitize any data before saving it to the database. This prevents malicious code and scripts from being run on your server. We’ll use the wp_filter_post_kses() function, which strips our dangerous code and allows through anything you can include a post.

if ( ! function_exists( '_js_code_save_metabox' ) ) {
	 * Save the metabox
	 * @param  int   $post_id The post ID
	 * @param  array $post    The post data
	function _js_code_save_metabox( $post ) {

		// Verify that our security field exists. If not, bail.
		if ( ! isset( $_POST[ '_js_code_form_metabox_process' ] ) ) {

		// Verify data came from edit/dashboard screen
		if ( ! wp_verify_nonce( $_POST[ '_js_code_form_metabox_process' ], '_js_code_form_metabox_nonce' ) ) {
			return $post->ID;

		// Verify user has permission to edit post
		if ( ! current_user_can( 'edit_post', $post->ID ) ) {
			return $post->ID;

		// Check that our custom fields are being passed along
		// This is the `name` value array. We can grab all
		// of the fields and their values at once.
		if ( ! isset( $_POST[ '_js_code_metabox' ] ) ) {
			return $post->ID;
		 * Sanitize the submitted data
		 * This keeps malicious code out of our database.
		 * `wp_filter_post_kses` strips our dangerous server values
		 * and allows through anything you can include a post.
		$sanitized = wp_filter_post_kses( $_POST[ '_js_code_metabox' ] );
		// Save our submissions to the database
		update_post_meta( $post->ID, '_js_code', $sanitized );


add_action( 'save_post', '_js_code_save_metabox', 1, 2 );

Saving revision history

This is optional, and potentially undesireable for certain data types, but you can save your field data to revision history. Restoring a a post to an old version will also update the metabox.

We’ll hook into the save_post action hook to do this.

if ( ! function_exists( '_js_code_save_revisions' ) ) {
	 * Save events data to revisions
	 * @param  Number $post_id The post ID
	function _js_code_save_revisions( $post_id ) {

		// Check if it's a revision
		$parent_id = wp_is_post_revision( $post_id );

		// If is revision
		if ( $parent_id ) {

			// Get the saved data
			$parent  = get_post( $parent_id );
			$details = get_post_meta( $parent->ID, '_js_code', true );

			// If data exists and is an array, add to revision
			if ( ! empty( $details ) ) {
				add_metadata( 'post', $post_id, '_js_code', $details );



add_action( 'save_post', '_js_code_save_revisions' );

Restoring from revision history

Naturally, when restoring a post, we’ll want to update the metabox field value as well.

We’ll hook into the wp_restore_post_revision action hook for this, updating the post meta value with the historic version.

if ( ! function_exists( '_js_code_restore_revisions' ) ) {
	 * Restore events data with post revisions
	 * @param  Number $post_id     The post ID
	 * @param  Number $revision_id The revision ID
	function _js_code_restore_revisions( $post_id, $revision_id ) {

		// Variables
		$post     = get_post( $post_id ); // The post
		$revision = get_post( $revision_id ); // The revision
		$details  = get_metadata( 'post', $revision->ID, '_js_code', true ); // The historic version

		// Replace our saved data with the old version
		update_post_meta( $post_id, '_js_code', $details );


add_action( 'wp_restore_post_revision', '_js_code_restore_revisions', 10, 2 );

Displaying your metabox field in the revisions view

If you want to be able to see the historic version in the revisions view, you’ll need two additional functions.

First, we’ll add a field to the revision fields with the _wp_post_revision_fields filter. (WARNING! This function’s access is marked private. This means it is not intended for use by plugin or theme developers, only in other core functions. It is listed here for completeness. )

if ( ! function_exists( '_js_code_get_revisions_fields' ) ) {
	 * Get the data to display on the revisions page
	 * @param  array $fields The fields
	 * @return array The fields
	function _js_code_get_revisions_fields( $fields ) {
		// Set a title
		$fields['_namespace'] = 'Some Item';
		return $fields;

add_filter( '_wp_post_revision_fields', '_js_code_get_revisions_fields' );

Next, we’ll tell WordPress to show that field on the revisions page with the `_wp_post_revision_field_my_meta` filter.

if ( ! function_exists( '_js_code_display_revisions_fields' ) ) {
	 * Display the data on the revisions page
	 * @param  String|array $value The field value
	 * @param  array        $field The field
	function _js_code_display_revisions_fields( $value, $field ) {
		global $revision;
		return get_metadata( 'post', $revision->ID, $field, true );

add_filter( '_wp_post_revision_field_my_meta', '_js_code_display_revisions_fields', 10, 2 );

Wrap up

In a future article, I’ll show you how to work with multiple fields, set defaults, and avoid over-taxing your database.

Now it’s time to code!

JSC0d3's Logo
About JSC0d3

JSC0d3 is an entrepreneur, online marketer, and an employee of an IT company. When not building websites, creating content, or helping customers improve their online business, spend time with their wife and two beautiful children. Although he still feels new in WordPress, he enjoys sharing what he has learned with all of you! If you want to get in touch with him, you can do this through this website.

On the same idea

Posted by | April 1, 2019

Images are vital components of every website Before you start questioning the importance of images, just try to imagine your favorite blog or website...

Posted by | March 5, 2019

I’ve been loosely following the noise and #wpdrama surrounding Gutenberg for as long as it has been around and honestly for the most part I’ve...

Posted by | February 24, 2019

To ensure that your site ranks highly in Search Engine Result Pages (SERPs), you’ll need to make it easy for search engine ‘bots’ to explore...

Previous PostBackNext Post
2 impressions on “Adding custom fields to posts and pages in WordPress


What if I need to sanitize multiple fields, how to achieve it?

This code returns string, but how about array?
$sanitized = wp_filter_post_kses( $_POST[ ‘_js_code_metabox’ ] )

J&S Code,

An option that will work in some cases, is to change the code to sanitize each part of the array separately, instead of all at once.

For example, an array like this:
foreach ( $input as $key => $val ) {

$new_input[ $key ] = ( isset( $input[ $key ] ) ) ?
sanitize_text_field( $val ) :


Leave here an impression