Sticky posts on home page, search, tag and archives without plugin

This same exact question was asked earlier this week or over the weekend, and it had me thinking. Here is the idea that I came up with.

If you look at the source code of the WP_Query class, you will see that sticky posts is only added to the first page of the home page. There is also no filter supplied to change this behavior in order to set the required templates according to your liking.

There are many ways to display sticky posts on other templates through widgets, custom queries, shortcodes (which I will not recommend due to the fact that using do_shortcode() is slower that using the function itself) or custom functions where you need to display them. I have opted to go with using the_posts filter and pre_get_posts action.

HERE’S HOW:

  • Get the post ID’s saved as sticky posts with get_option( 'sticky_posts' )

  • Remove this posts from the main query with pre_get_posts. As stickies are included on the home page, we will exclude the home page. We will also exclude normal pages

  • Inside a custom function, run a custom query with get_posts to get the posts set as sticky posts.

  • Merge the returned array of sticky posts with the current returned posts of the main query with array_merge

  • Hook this custom function to the_posts filter

Here is the code: (Requires PHP 5.4+)

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin()
         && $q->is_main_query()
         && !$q->is_home()
         && !$q->is_page()
    ) {

        $q->set( 'post__not_in', get_option( 'sticky_posts' ) );

        if ( !$q->is_paged() ) {
            add_filter( 'the_posts', function ( $posts )
            {
                $stickies = get_posts( ['post__in' => get_option( 'sticky_posts' ), 'nopaging' => true] );

                $posts = array_merge( $stickies, $posts );

                return $posts;

            }, 10, 2);
        }

    }
});

Leave a Comment