Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I'm able to easily include my custom post types into my main loop by making small adjustments with query_posts(), but I'm not sure how I would go about including custom post types in the "Recent Posts" sidebar widget (or any of the other widgets, for that matter).

How should I go about expanding "Recent Posts" scope to include more than just the native post type?

share|improve this question
add comment

5 Answers

up vote 6 down vote accepted

You'll have to edit the code for the Recent Posts widget or create your own version based on the default. The code is in the wp-includes/default-widgets.php file around line 513. But since you should never make modifications to core, my recommendation would be to copy the code to create your own My Custom Recent Posts widget and use that on your site. Just drop the new widget class into your theme's functions.php file or use it in a plugin.

The only real modification you need to make are to the widget's class name and encapuslated functions and options (so that there aren't any naming conflicts with the original Recent Posts widget. After that, you'll need to edit the call to WP_Query in the widget() constructor so that it includes your custom post type.

For this example, I've set post_type equal to array('post, 'page', 'custom-post-type') ... you'll need to modify that to fit your specific use case.

Here's the widget's full code for reference:

  * My_Custom_Recent_Posts widget class
class WP_Widget_My_Custom_Recent_Posts extends WP_Widget {

    function WP_Widget_My_Custom_Recent_Posts() {
        $widget_ops = array('classname' => 'widget_my_custom_recent_entries', 'description' => __( "The most recent posts on your site") );
        $this->WP_Widget('my-custom-recent-posts', __('My Custom Recent Posts'), $widget_ops);
        $this->alt_option_name = 'widget_my_custom_recent_entries';

        add_action( 'save_post', array(&$this, 'flush_widget_cache') );
        add_action( 'deleted_post', array(&$this, 'flush_widget_cache') );
        add_action( 'switch_theme', array(&$this, 'flush_widget_cache') );

    function widget($args, $instance) {
        $cache = wp_cache_get('widget_my_custom_recent_posts', 'widget');

        if ( !is_array($cache) )
            $cache = array();

        if ( isset($cache[$args['widget_id']]) ) {
            echo $cache[$args['widget_id']];


        $title = apply_filters('widget_title', empty($instance['title']) ? __('My Custom Recent Posts') : $instance['title'], $instance, $this->id_base);
        if ( !$number = (int) $instance['number'] )
            $number = 10;
        else if ( $number < 1 )
            $number = 1;
        else if ( $number > 15 )
            $number = 15;

        $r = new WP_Query(array('showposts' => $number, 'nopaging' => 0, 'post_status' => 'publish', 'ignore_sticky_posts' => true, 'post_type' => array('post', 'page', 'custom-post-type')));
        if ($r->have_posts()) :
        <?php echo $before_widget; ?>
        <?php if ( $title ) echo $before_title . $title . $after_title; ?>
        <?php  while ($r->have_posts()) : $r->the_post(); ?>
        <li><a href="<?php the_permalink() ?>" title="<?php echo esc_attr(get_the_title() ? get_the_title() : get_the_ID()); ?>"><?php if ( get_the_title() ) the_title(); else the_ID(); ?></a></li>
        <?php endwhile; ?>
        <?php echo $after_widget; ?>
        // Reset the global $the_post as this query will have stomped on it


        $cache[$args['widget_id']] = ob_get_flush();
        wp_cache_set('widget_my_custom_recent_posts', $cache, 'widget');

    function update( $new_instance, $old_instance ) {
        $instance = $old_instance;
        $instance['title'] = strip_tags($new_instance['title']);
        $instance['number'] = (int) $new_instance['number'];

        $alloptions = wp_cache_get( 'alloptions', 'options' );
        if ( isset($alloptions['widget_my_custom_recent_entries']) )

        return $instance;

    function flush_widget_cache() {
        wp_cache_delete('widget_my_custom_recent_posts', 'widget');

    function form( $instance ) {
        $title = isset($instance['title']) ? esc_attr($instance['title']) : '';
        if ( !isset($instance['number']) || !$number = (int) $instance['number'] )
            $number = 5;
        <p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:'); ?></label>
        <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo $title; ?>" /></p>

        <p><label for="<?php echo $this->get_field_id('number'); ?>"><?php _e('Number of posts to show:'); ?></label>
        <input id="<?php echo $this->get_field_id('number'); ?>" name="<?php echo $this->get_field_name('number'); ?>" type="text" value="<?php echo $number; ?>" size="3" /></p>
share|improve this answer
Instead of renaming the widget, I think doing unregister_widget to the default widget before registering the new one would be better (less confusing in the admin). –  sorich87 Sep 30 '10 at 15:25
Yes, you could use unregister_widget, but this method gives you access to both should you want to use both (i.e. have a Recent Posts widget to display recent blog posts and a Recent Movie Reviews widget to display only recent entries of a specific custom post type). –  EAMann Sep 30 '10 at 15:31
just a note to anyone trying out the code above: Don't forget to add this: register_widget('WP_Widget_My_Custom_Recent_Posts'); –  Fredrik Holm Feb 24 '11 at 13:28
Thanks for providing the code for the custom widget class and for the explanations. It's come in very useful. However I was just wandering if there was a way that I could alter it to allow for embedded HTML in the title field? Basically I need to turn the widget title into a clickable link but by default embedded HTML is stripped out of widget titles. Any help would be much appreciated. –  Ian Apr 29 '11 at 9:57
I don't know the first thing about PHP but couldn't one just Extend the default class and update the required variables? Rather than a full cut-and-paste? –  thornomad Mar 10 '12 at 21:02
show 1 more comment

As of at least 3.6, you can use the following code to modify the query used:

add_filter('widget_posts_args', 'widget_posts_args_add_custom_type'); 
function widget_posts_args_add_custom_type($params) {
   $params['post_type'] = array('post','custom_type');
   return $params;

Just add the types you want in the array for post_type and they should appear.

Update: According to http://core.trac.wordpress.org/ticket/16159, this has been available since 3.4

share|improve this answer
add comment

I just came across a great plugin where the heavy lifting is already done, and it has great documentation and author support. I've really been impressed.

It allows WP_Query overrides (allowing you to filter by custom post types and anything else you would want) and some clear instructions on how to use it.

Documentation http://www.pjgalbraith.com/2011/08/recent-posts-plus/

Wordpress Plugin URL http://wordpress.org/extend/plugins/recent-posts-plus/

Made my work just that much shorter!

share|improve this answer
add comment

You can copy the widget code (see /wp-includes/default-widgets.php) and modify the query line.

share|improve this answer
My thoughts exactly ... but I was already halfway through writing my answer when you posted this ... –  EAMann Sep 30 '10 at 15:13
Yours is very complete! +1 –  sorich87 Sep 30 '10 at 15:26
add comment

I have also create a widget plugin for this that's more customizable than the Recent Posts widget. If interested you can download it here http://new2wp.com/pro/latest-custom-post-type-posts-sidebar-widget/

share|improve this answer
add comment

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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