I'm using Black Studio TinyMCE Widget as a rich text editor widget. In a sidebar I inserted the TinyMCE widget with a [testimonial] shortcode and some content after it.

For example:


Read more client testimonials (as a link)

When I switch to the HTML tab of that widget I've got the following:

<p><a title="Testimonials" href="http://mm.dev/testimonials/">Read more client testimonials</a></p>

The shorcode simply displaying a random Testimonial CPT post:

add_shortcode("testimonial", "dlma_testimonial_shortcode");
function dlma_testimonial_shortcode($atts, $content = null){
    $args = array(
        'post_type' => 'testimonial',
        'post_status' => 'publish',
        'posts_per_page' => '1',
        'orderby' => 'rand'
    $testimonial = new WP_Query($args);

        return apply_filters('the_content', $testimonial->posts[0]->post_content);
    return "";

However, when I view a page, stray <p> elements are inserted:
Edited thanks to Tom J Nowell

<div class="textwidget">
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis vestibulum velit convallis sem pulvinar vitae lobortis metus varius.” <em>Person name, Person job</em></p>
    <a href="http://mysite.com/testimonials/" title="Testimonials">Read more client testimonials</a>

The [testimonial] shortcode got expanded correctly, however, as it was originally wrapped into <p> element in the widget it still appears to be wrapped in it. I have tried deleting the <p> element from the widget HTML tab view, however, whenever the Save button is clicked the <p> element is inserted again.

I've tried to remove unwanted <p> element that wraps the shortcode with the the_content filter as following:

remove_filter( 'the_content', 'wpautop' );
add_filter( 'the_content', 'wpautop' , 12);

That didn't work. I'm guessing I've got the flow wrong of processing the content of a widget with a shortcode in it. I'm very confused now. How can I remove unwanted <p> elements that wrap shortcodes?

I would hugely appreciate any help!

Many thanks.

  • 1
    if you call apply_filters('the_content' you don't need to use do_shortcode since its one of the the_content filters.
    – Bainternet
    Commented Apr 15, 2012 at 15:50
  • Thanks Bainternet, I find the_content filter a bit confusing...as you can see.
    – dashaluna
    Commented Apr 15, 2012 at 16:45

6 Answers 6


There actually are several ways to handle the Wordpress editor wrapping shortcodes in <p> tags.

This code shows probably the simplest way to do it...just a simple and short function you need to drop into your functions.php file. Once you do, no more tags around your shortcodes that are on their own line!

function wpex_clean_shortcodes($content){   
$array = array (
    '<p>[' => '[', 
    ']</p>' => ']', 
    ']<br />' => ']'
$content = strtr($content, $array);
return $content;
add_filter('the_content', 'wpex_clean_shortcodes');

Source: http://www.wpexplorer.com/snippet/clean-wordpress-shortcodes

Hope that helps someone!


This is a known issue since years. Please have a look at Wordpress Ticket.

As others maybe mentioned before, there is Plugin to fix this issue. It is called "Shortcode Empty Paragraph Fix". Its not the best solution but not so bad.

Im not a fan of putting a lot of bloat to my wp installation - so i wrapped the shortcode in div without a class and fixed the problem.

<div> [woocommerce_cart] </div>

I hope the issue will adressed in the next few years ;) so i can remove my divs - in my opinion a saver way, because sometimes extra functions in the funcitons.php can cause some problems down the road.


A blockquote is a block element, and shouldn't be put inside a <p> tag, what you're seeing is your browsers DOM trying to compensate for the invalid html markup. If you look at the raw source code itself generated, you will not find those stray paragraph tags

Remove your <p> wrapping tags, and make sure any content that needs wrapping in a <p> tag is done inside the shortcode.


The general rule is that a <p> element is a block element, but it should never contain block elements, only inline elements.


  • A p element can contain:
    • span
    • b
    • strong
    • etc
  • A p element cannot contain:
    • div
    • blockquote
    • ul
    • ol
    • h1
    • etc
  • Thanks for the answer. I've checked raw HTML page code and you're right, there isn't stray <p> elements..I will update the code in the question. However, I did try to delete wrapping <p> elements around the shortcode in the widget, however, after saving the widget content the <p> element is inserted again, so I do still need to somehow filter them out.. any ideas?
    – dashaluna
    Commented Apr 15, 2012 at 15:39
  • It's the auto p stuff, you should reformat your content accordingly so that the shortcode doesnt not have a blank line immediatley after it
    – Tom J Nowell
    Commented Apr 15, 2012 at 16:51
  • Tom, I've tried reformatting in the widget, it either wraps shortcode and link in a separate <p> element, or into a single one if there is no space between shortcode and link. But it still wraps the content... I don't think the formatting in the widget would help. I think I need to do it with filters, but can't figure out to do it correctly :(
    – dashaluna
    Commented Apr 15, 2012 at 17:15
  • Well to begin with there is no need at all for there to be any <p> tags in the post content at all, Wordpress automatically puts those in, so you may be having the same identical issue with the second paragraph because WordPress generates <p><p>stuff</p></p> which is also invalid html and would probably generate this in the dom: <p></p><p>stuff<p><p></p>
    – Tom J Nowell
    Commented Apr 15, 2012 at 18:25
  • Try this all on one line: "[testimonial]Read more client testimonials (as a link)"
    – Tom J Nowell
    Commented Apr 15, 2012 at 18:26

I would suggest this, which moves specific 'block-level' shortcodes outside of 'p' elements without affecting those elsewhere that may be intended to be inline:

function my_fix_shortcodes($content){  
  if (strpos($content, '<p>') !== false) {
    /* Move 'block level' shortcode tags outside <p> elements by surrounding with </p>...<p>
       If they have content, this will be put inside a new <p>
       - you could use a final regex to remove this if needed... */
    $block_level_shortcodes = array(
    $regex_shortcode = '(' . implode('|', $block_level_shortcodes) . ')';
    // Match opening/standalone or closing shortcode tags
    $regex = '/(\[' . $regex_shortcode . '[^\]]*\]|\[\/' . $regex_shortcode . '\])/';
    $regex_count = 0;
    $content = preg_replace($regex, '</p>$1<p>', $content, -1, $regex_count);
    if ($regex_count > 0) {
      /* Remove <br/>s at start or end of <p> which might now be present,
         then any empty <p>s which quite probably are */
      $content = preg_replace(
        array('/<p>\s*<br *\/>/s', '/<br *\/>\s*<\/p>/s', '/<p>\s*<\/p>/s'),
        array('<p>', '</p>', ''),
  return $content;
add_filter('the_content', 'my_fix_shortcodes');

Also note that there seems to be a failed attempt to resolve this sort of issue within WordPress [4.00] itself, so your shortcode handlers may be passed $content that is prepended with </p> and postfixed with <p>, which you will have to detect and remove.

Update: there seems to be something broken within WordPress itself (relating to sporadic addition of <p> and </p>, outside of the straightforward initial wpautop, when shortcodes are involved, as alluded to above [but those sporadic elements are not passed to your handler]) which means this solution may not always work, even though it should :/


For a shortcode use the "custom HTML" block.


Maybe this can help you (or others too):

// remove <br> tags from text widget content, from 4.8 version WP adds these tags
remove_filter('widget_text_content', 'wpautop');

Your Answer

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

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