How to Create a Custom WordPress Shortcode for Displaying Recent Posts

Luca R Dec 20, 2024 Shortcodes
How can I make a special code that shows my latest blog posts on any page of my website?
What is the process for creating a custom WordPress shortcode that dynamically retrieves and displays a list of recent posts, including options for post count and post types?
Andy answered Dec 20, 2024

Creating a Custom Recent Posts Shortcode

Basic Implementation

Let's create a shortcode that displays recent posts with customizable options. Here's a step-by-step solution:

First, register the shortcode in your theme's functions.php file:

function recent_posts_shortcode($atts) {
    // Default parameters
    $args = shortcode_atts(array(
        'posts' => 5,
        'type' => 'post',
        'category' => '',
        'excerpt' => 'yes',
    ), $atts);
    
    // Query setup
    $query = new WP_Query(array(
        'post_type' => $args['type'],
        'posts_per_page' => $args['posts'],
        'category_name' => $args['category'],
        'post_status' => 'publish',
    ));
    
    // Start output buffering
    ob_start();
    
    if($query->have_posts()) :
        echo '<ul class="recent-posts-list">';
        while($query->have_posts()) : $query->the_post();
            echo '<li>';
            echo '<h3><a href="' . esc_url(get_permalink()) . '">' . get_the_title() . '</a></h3>';
            if($args['excerpt'] === 'yes') {
                echo '<div class="excerpt">' . get_the_excerpt() . '</div>';
            }
            echo '</li>';
        endwhile;
        echo '</ul>';
    endif;
    
    wp_reset_postdata();
    return ob_get_clean();
}
add_shortcode('recent_posts', 'recent_posts_shortcode');

Usage Examples

You can use the shortcode in various ways:

Basic usage:

[recent_posts]

With parameters:

[recent_posts posts="3" type="post" category="news" excerpt="no"]

Adding Styling

Add these CSS styles to your theme's stylesheet:

.recent-posts-list {
    list-style: none;
    padding: 0;
    margin: 20px 0;
}

.recent-posts-list li {
    margin-bottom: 20px;
}

.recent-posts-list h3 {
    margin-bottom: 10px;
}

.recent-posts-list .excerpt {
    font-size: 0.9em;
    color: #666;
}

Security Considerations

  1. Always sanitize inputs:
  • Use shortcode_atts() to validate parameters
  • Use esc_url() for URLs
  • Use esc_html() for text output
  1. Prevent SQL injection:
  • Use WP_Query instead of direct SQL queries
  • Validate numerical inputs

Best Practices

  1. Always use wp_reset_postdata() after custom queries
  2. Use output buffering for clean code organization
  3. Include error handling
  4. Make the shortcode parameters flexible but with sensible defaults

Enhanced Version with More Features

Here's an advanced version with additional features:

function advanced_recent_posts_shortcode($atts) {
    // Default parameters
    $args = shortcode_atts(array(
        'posts' => 5,
        'type' => 'post',
        'category' => '',
        'excerpt' => 'yes',
        'excerpt_length' => 20,
        'thumbnail' => 'yes',
        'date' => 'yes',
        'author' => 'no',
        'class' => '',
    ), $atts);
    
    // Query setup
    $query = new WP_Query(array(
        'post_type' => sanitize_text_field($args['type']),
        'posts_per_page' => intval($args['posts']),
        'category_name' => sanitize_text_field($args['category']),
        'post_status' => 'publish',
    ));
    
    $output = '';
    
    if($query->have_posts()) {
        $class = $args['class'] ? ' ' . esc_attr($args['class']) : '';
        $output .= '<div class="advanced-recent-posts' . $class . '">';
        
        while($query->have_posts()) : $query->the_post();
            $output .= '<article class="post-item">';
            
            if($args['thumbnail'] === 'yes' && has_post_thumbnail()) {
                $output .= '<div class="post-thumbnail">';
                $output .= get_the_post_thumbnail(null, 'thumbnail');
                $output .= '</div>';
            }
            
            $output .= '<div class="post-content">';
            $output .= '<h3><a href="' . esc_url(get_permalink()) . '">' . esc_html(get_the_title()) . '</a></h3>';
            
            if($args['date'] === 'yes' || $args['author'] === 'yes') {
                $output .= '<div class="post-meta">';
                if($args['date'] === 'yes') {
                    $output .= '<span class="date">' . get_the_date() . '</span>';
                }
                if($args['author'] === 'yes') {
                    $output .= '<span class="author">by ' . get_the_author() . '</span>';
                }
                $output .= '</div>';
            }
            
            if($args['excerpt'] === 'yes') {
                $output .= '<div class="excerpt">' . wp_trim_words(get_the_excerpt(), $args['excerpt_length']) . '</div>';
            }
            
            $output .= '</div>'; // .post-content
            $output .= '</article>';
        endwhile;
        
        $output .= '</div>';
    }
    
    wp_reset_postdata();
    return $output;
}
add_shortcode('advanced_recent_posts', 'advanced_recent_posts_shortcode');

Alternative Solutions

If you prefer not to code, these plugins offer similar functionality:

  1. Display Posts Shortcode
  1. Recent Posts Widget Extended

Common Pitfalls to Avoid

  1. Don't forget to reset post data
  2. Avoid deep nesting of queries
  3. Don't hardcode HTML without escape functions
  4. Be mindful of performance with large post counts
  5. Consider pagination for large datasets

Remember to test your shortcode thoroughly, especially when using it with different themes and page builders.