Adding custom shortcode to page leads to page without styling

by Elian   Last Updated April 19, 2018 16:08 PM

Scenario i wanted to build

Reusable components people can use several times inside my WordPress environment.

My solution for the problem

I made a custom post type named "Componenten". I made a custom shortcode to display only one post from within the "Componenten" post type, using the name of the post as the shortcode parameter. Here is my code for the shortcode and the following function:

<?php

/**
 * Register all shortcodes
 *
 * @return null
 */
function register_shortcodes() {
    add_shortcode( 'component', 'shortcode_components' );
}
add_action( 'init', 'register_shortcodes' );

function shortcode_components( $atts ) {
    global $wp_query,
           $post;
    $component_name = $atts['naam'];

    $loop = new WP_Query( array(
        'post_type'         => 'componenten',
        'name'              => $component_name
    ) );

    if( ! $loop->have_posts() ) {
        return false;
    }

    while( $loop->have_posts() ) {
        $loop->the_post();
        return the_content();
    };

    wp_reset_postdata();
}

Problem

The shortcode works fine, it retrieves the custom post type by it's name and displays it. However; when i update the page within wordpress with the shortcode, for example [component naam="test"] and i update the page, it redirects to a page displaying the post, without any styling. It does not loop back to the admin Edit page i was adding the shortcode to. Every other page works fine. It's only happening when i add my custom shortcode. So the problem is in my shortcode somewhere.

Things i've tried

I've commented out different parts of the function to see what was going on. The problem isn't happening when i comment out:

while( $loop->have_posts() ) { $loop->the_post(); return the_content(); };

But, needless to say, my shortcode of course doesn't work anymore.



Answers 1


Shortcodes need to return their values, but the_content() echoes the post content.

So you have two options. First is to capture all output and then return it at the end:

ob_start(); // Start capturing output;

while( $loop->have_posts() ) {
   $loop->the_post();
   the_content();
};

wp_reset_postdata();

return ob_get_clean(); // Return captured output;

That will be the cleanest way if you want to capture HTML and the output of template tags or partials.

The other option is to get the content into a variable. The 'get' equivalent of the_content() is get_the_content(), but it tries weird stuff with the 'Read More' tag etc. so the easiest way is to get the raw $post->post_content with the the_content filters applied:

global $post;

// etc. etc.

while( $loop->have_posts() ) {
   $loop->the_post();
   $content = apply_filters( 'the_content', $post->post_content );
};

wp_reset_postdata();

return $content;

Also note how instead of returning inside the loop I put the value in a variable and returned it at the end. This is because if you return earlier in the function then wp_reset_postdata() will never run, which could cause trouble with other loops on the page.

Jacob Peattie
Jacob Peattie
April 19, 2018 15:51 PM

Related Questions


Plugin Error on activating

Updated April 30, 2015 21:03 PM

Shortcode with WP_Query more than once on one page

Updated February 25, 2017 22:08 PM

Shortcode for display posts in wp-editor

Updated August 11, 2017 12:08 PM

Shortcode for show all custom posts in a single page

Updated October 04, 2017 09:08 AM