i am building a custom meta box having a classic editor input rather than just input. The reason for using wp_editor is that I wanted to add paragraphs of text inside div but the simple input escaped the html. Now i am using this function. Now the problem is that when I output the data of this editor input, it escapes html. Here is the code I use for output.

echo get_post_meta($post_id, 'skyscraper_post', true );

I use this code for adding meta boxes.

add_action( 'add_meta_boxes_post', function ( $post ) {
    if ( $post->_wp_page_template === 'page-templates/skyscraper-post.php' ) {
        add_meta_box( 'sky_post_excerpt', 'SkyScraper Post Excerpt and Links', 'sky_post_excerpts', 'post', 'advanced', 'high' );
function sky_post_excerpts() {
     global $post;
    $values = get_post_custom( $post->ID );
    $data = get_post_meta($post->ID, 'skyscraper_post', true);

    $strong_title = isset( $values['skyscraper_strong'] ) ? esc_html( $values['skyscraper_strong'][0] ) : "";
    $title = isset( $values['skyscraper_post_title'] ) ? esc_attr( $values['skyscraper_post_title'][0] ) : "";
    $text = isset( $values['skyscraper_post'] ) ?  $values['skyscraper_post'][0]  : "";
    $image = isset( $values['skyscraper_post_image'] ) ? esc_attr( $values['skyscraper_post_image'][0] ) : "";

    // We'll use this nonce field later on when saving.
    wp_nonce_field( 'my_post_meta_box_nonce', 'post_meta_box_nonce' );

    <table class="form-table">
        <tr valign="top">
            <th scope="row">
                <label><strong>Skyscraper Title</strong></label>
                <p><input class="widefat" name="skyscraper_strong" id="skyscraper_strong" ><?php echo $strong_title; ?></input>
                <p><input class="widefat" rows="4" name="skyscraper_post_title" id="skyscraper_post_title" value="<?php echo $title; ?>"/>

        <tr valign="top">
            <th scope="row">
                <label for="skyscraper_post"><strong>Skyscraper Page Excerpt</strong></label>
                <?php wp_editor( $data, 'post_meta_box', $settings = array('textarea_name'=>'skyscraper_post')); ?>
        <tr valign="top">
            <th scope="row">
                <label for="skyscraper_image"><strong>SVG Image Link</strong></label>
                <p><input class="widefat" rows="4" name="skyscraper_post_image" id="skyscraper_post_image" value="<?php echo $image; ?>"/>


To save the data I use this code.

    add_action( 'save_post', 'post_meta_box_save' );
    function post_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['post_meta_box_nonce'] ) || !wp_verify_nonce( $_POST['post_meta_box_nonce'], 'my_post_meta_box_nonce' ) ) return;

    // if our current user can't edit this post, bail
    if( !current_user_can( 'edit_post' ) ) return;
    //$allowed = wp_kses_allowed_html();
    $allowed= array(
        'p' => array(
            'class' => array(),
            'id' => array(),
        'strong' => array(),

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

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

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

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

    $_POST['skyscraper_post_title'], $allowed ) );

I don't know why the html is being escaped. Any help will be appreciated, Thanks.

here is the escaped output. enter image description here

  • This part escapes the HTML code: htmlspecialchars($_POST['skyscraper_post']). Perhaps you could just remove that and the htmlspecialchars_decode() ?
    – Sally CJ
    Apr 5, 2019 at 4:45
  • after removing this it still escapes the html. Any other way to output the data Apr 5, 2019 at 4:51
  • Have you tried to decode it using wp_specialchars_decode instead of htmlspecialchars_decode? Also you could use the sanitize_text_field instead of wp_kses to sanitize the $_POST data. Apr 5, 2019 at 4:54
  • applied the methods but still escaping p tags. Apr 5, 2019 at 4:59
  • is there any other way to output data of wp_editor other than get post meta Apr 5, 2019 at 5:10

1 Answer 1


This may not solve the problem, but I hope it helps you.

  1. In the sky_post_excerpts() function, I used this to display the TinyMCE/classic editor: (the $data is get_post_meta($post->ID, 'skyscraper_post', true))

    <?php wp_editor( $data, 'post_meta_box', array('textarea_name'=>'skyscraper_post')); ?>
  2. In the post_meta_box_save() function, I saved the meta like so, where $allowed is wp_kses_allowed_html():

    update_post_meta( $post_id, 'skyscraper_post', wp_kses( $_POST['skyscraper_post'], $allowed ) );
  3. On the front-end, I display the metadata like so:

    // This is really just an example. And I was on a single post.
    echo get_post_meta( get_the_ID(), 'skyscraper_post', true );

And everything worked well for me — all HTML remained as generated by the TinyMCE editor.


if I use visual not text in TinyMCE then the p tags doesn't show

wp_kses_allowed_html() when called without specifying the $context parameter, will use the global variable $allowedtags which is an array of KSES allowed HTML elements, which (by default) do not include p tags/elements.

If you want to allow p elements, you can:

  • Use wp_kses_allowed_html( 'post' ) which will use the global variable $allowedposttags which also holds similar array as the $allowedtags variable, but $allowedposttags has many elements allowed including table and video.

  • Or manually enable the p elements:

    $allowed = wp_kses_allowed_html();
    $allowed['p'] = array(); // allows all attributes!
  • Same as above, but build your own allowed tags:

    $allowed = array();
    $allowed['p'] = array(); // allows all attributes!

But, you shouldn't allow all attributes.. so:

$allowed['p'] = array(
  'class' => true,
  'id'    => true,
  ...other attributes...
  • i got it but if I use visual not text in TinyMCE then the p tags doesn't show. Apr 5, 2019 at 9:06
  • let me update my code. Apr 5, 2019 at 9:08
  • I've just seen your question's edit. So are the p tags still being escaped? And I've also updated my answer - you already know some, but the update might still help you. Let me know.
    – Sally CJ
    Apr 5, 2019 at 16:52
  • it helped. also i just used wp_kses_post() instead. However, thanks for your help, i learned a lot from you. again,thanks. Apr 6, 2019 at 5:37
  • I'm glad I could help. :)
    – Sally CJ
    Apr 6, 2019 at 14:39

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.