I created a new page for the homepage settings of my theme using add_pages_page(). On that page I want to use wp.media to let the user select one or several images from the library (or upload new ones). I thought the way to go was meta boxes but I can't get the box to display. Here's how I tried to add the meta box:

function register_homepage_subpage() {
  add_pages_page( 'Customize Homepage', 'Homepage', 'manage_options', 'my_homepage', 'my_homepage_callback' );
add_action( 'admin_menu', 'register_homepage_subpage' );

function my_homepage_callback() {

function banner_meta_box() {
    $screen_id = get_current_screen()->id;
    add_meta_box("banner-meta-box", __("Banner Options", "textdomain"), "banner_meta_box_callback", $screen-id);
add_action( 'add_meta_boxes', 'banner_meta_box' );

function banner_meta_box_callback() {
  echo "<h2>My Title</h2>"; // this doesn't display

This answer suggests meta boxes are only for posts types which got me confused.
I don't know if my "menu slug conforms to the limits of sanitize_key()" as indicated here.
Or am I using the wrong hook?
Thanks for reading + helping :)

2 Answers 2


You are trying to create a new admin page with add_pages_page(). Meta boxes are only for existing post types, not for admin pages. You need to create your fields like on a normal admin page.

You should read the official codex about this first: Creating Options Pages

For a simple start take a look at this example:

function register_homepage_subpage() {
    add_pages_page( 'Customize Homepage', 'Homepage', 'manage_options', 'my_homepage', 'my_homepage_callback' );

    //call to register settings function, to create our settings group
    add_action( 'admin_init', 'register_homepage_settings' );
add_action( 'admin_menu', 'register_homepage_subpage' );

We create your admin page like you did already in your code. We also add an add_action call to another function which will register your new admin settings:

function register_homepage_settings() {
    //register our settings
    register_setting( 'my-homepage-settings-group', 'my_option' );

After this you can now start your callback function:

function my_homepage_callback() { ?>
    <div class="wrap">
        <h1>My Title</h1>

        <form method="post" action="options.php">

            <?php // get registered fields and section
            settings_fields( 'my-homepage-settings-group' ); 
            do_settings_sections( 'my-homepage-settings-group' ); ?>

            <table class="form-table">
                <tr valign="top">
                    <th scope="row">My Field</th>
                    <td><input type="text" name="my_option" value="<?php echo esc_attr( get_option('my_option') ); ?>" /></td>

            <?php submit_button(); ?>

<?php }

We need the form created in this function and also the submit_button(); to be able to save the new field.

We get our registered settings with settings_fields( 'my-homepage-settings-group' );.

After you added this code, you should see an heading and also a new text-field on your admin page. You can enter data and save it already.

To create an media upload / image select field you will need some more code and also some jQuery.

First you need a new button to open the wp.media window. In your callback function, add a new button directly after (this is important later on) our text field:

    <input type="text" name="my_option" value="<?php echo esc_attr( get_option('my_option') ); ?>" />
    <input type="button" class="button upload_image_button" value="Add Image" />

Than we have to enqueue the default wp_enqueue_media(); and also another JS file which will hold our code for the wp.media window.

function prfx_admin_enqueue() {
    // enqueue default WP media uploader

    // enqueue our media uploader script
    wp_enqueue_script( 'prfx-media-script', plugins_url('scripts.js', __FILE__) );
add_action( 'admin_enqueue_scripts', 'prfx_admin_enqueue' );

Now we need to create the scripts.js file and its content:

    // if button with class upload_image_button is clicked
    $( '.upload_image_button' ).on('click', function() {

        var mediaUploader;
        var button = $(this); // the button with the class uploaded_image_button

        // If the uploader object has already been created, reopen the dialog
        if (mediaUploader) {

        // Extend the wp.media object
        mediaUploader = wp.media.frames.file_frame = wp.media({
            title: 'Choose Image', // title text
            button: { text: 'Add Image' }, // but7on text
            library: { type: 'image' }, // type
            multiple: false // only one selection

        // When a file is selected, grab the URL and set it as the text field's value
        mediaUploader.on('select', function() {
            var attachment = mediaUploader.state().get('selection').first().toJSON();

            // find the text input field (previous element from button) and add the new value (URL from media)

        // Open the uploader dialog

        return false;

If you now click the new button, a normal WP media window should open. If you select an image and click on "Add Image" the URL from the image should be added to the text-field as the new value.


You can implement meta boxes on other non-post_type admin pages but you need to manually call do_meta_boxes() where you want them to output. You also need to make sure that add_meta_box() action is run prior.

You can see an example of this in /wp-admin/edit-form-comment.php...

do_action( 'add_meta_boxes', 'comment', $comment );
do_action( 'add_meta_boxes_comment', $comment );
do_meta_boxes( null, 'normal', $comment );

They ran two do_actions() but you can just use one if needed and it can be something custom like do_action( 'add_meta_boxes_banner', $someArgumentIfNeeded ). Basically, with your current code the add_meta_box action is never being triggered because the custom page doesn't run a do_action() for it.

For the JS sortability and expand/collapse you will also want to make sure to enqueue post.js. You will need post.js and postbox.js (the latter of which will come along with post.js) which you can enqueue like this...

wp_enqueue_script( 'post' );

And wrap your output somewhere with the class metabox-holder on a parent div to fix some broken styles...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.