Hide first blog post in content and output it in widget

Here is a cleaner method which does not skip the first post in the loop

As this is the main query on the home page, we can do the following:

  • Use pre_get_posts to alter the main query on the homepage so as to set an offset of one so that the newest post is ignored.

  • Recalculate the amount of posts found so that pagination works correctly in calculating the amount of pages to show.

  • Offset each page by one because we have excluded one post from page 1

Here is the code which goes into your functions.php: (NOTE: This code needs at least PHP 5.3 as it uses anonymous functions/closures. If you are using older PHP versions, upgrade your PHP version, versions PHP 5.3 and lower is dead)

add_action( 'pre_get_posts', function ( $q ) {

    if( !is_admin() && $q->is_main_query() && $q->is_home() ) {

        $offset = 1;
        $ppp = get_option( 'posts_per_page' );

        if ( !$q->is_paged() ) {

            $q->set( 'offset', $offset );

        } else {

            $offset = $offset + ( ( $q->query_vars['paged']-1 ) * $ppp );
            $q->set( 'offset', $offset );

        }

    }
    return $q;

});

//Recalculate found_posts for the home page
add_filter( 'found_posts', function ( $found_posts, $q ) {
    $offset = 1;

    if( $q->is_home() && $q->is_main_query() ) {
        $found_posts = $found_posts - $offset;
    }
    return $found_posts;

}, 10, 2 );

This will now take care to eliminate the newest post from the homepage loop. Now for the widget

I have quickly modified one of my widgets for this purpose to display the latest/newest post. You will just need to modify it to suite your exact needs, and also apply your own markup and styling

I’m not going to go into the details here as the Widget API is quite extensive. You will need to go and study this in your own time

Here is the widget: (NOTE: This widget needs at least PHP 5.4)

/**
 * Newest_Post_Widget widget class
 *
 * Displays the newest post
 *
 * @since 1.0.0
*/
class Newest_Post_Widget extends WP_Widget {

    public function __construct() {
        parent::__construct(
            'widget_newest_posts', 
            _x( 'Newest Post Widget', 'Tagged Posts Widget' ), 
            [ 'description' => __( 'Displays the newest post.' ), 
            ]
        );
        $this->alt_option_name="widget_newest_posts";

        add_action( 'save_post', [$this, 'flush_widget_cache'] );
        add_action( 'deleted_post', [$this, 'flush_widget_cache'] );
        add_action( 'switch_theme', [$this, 'flush_widget_cache'] );
    }

    public function widget( $args, $instance ) {
        $cache = [];
        if ( ! $this->is_preview() ) {
            $cache = wp_cache_get( 'widget_new_posts', 'widget' );
        }

        if ( ! is_array( $cache ) ) {
            $cache = [];
        }

        if ( ! isset( $args['widget_id'] ) ) {
            $args['widget_id'] = $this->id;
        }

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

        ob_start();

        $title          = ( ! empty( $instance['title'] ) ) ? $instance['title'] : __( 'Newest Post' );
        /** This filter is documented in wp-includes/default-widgets.php */
        $title          = apply_filters( 'widget_title', $title, $instance, $this->id_base );

        /**
         * Filter the arguments for the Newest Post widget.
         *
         * @since 1.0.0
         *
         * @see WP_Query::get_posts()
         *
         * @param array $args An array of arguments used to retrieve the newest post
         */
        $query_args = [
            'posts_per_page'    => 1,
        ];
        $q = new WP_Query( apply_filters( 'newest_post_args', $query_args ) );

        if( $q->have_posts() ) {

            echo $args['before_widget'];
            if ( $title ) {
                echo $args['before_title'] . $title . $args['after_title'];
            }               

            while( $q->have_posts() ) {
                $q->the_post(); ?>

                <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>> 

                    <header class="entry-header">
                        <?php the_title( '<h1 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h1>' ); ?>
                    </header><!-- .entry-header -->

                    <?php 
                    if ( has_post_thumbnail() ) { ?>

                        <div class="post-thumbnail">

                            <?php the_post_thumbnail(); ?>

                        </div><!--/.post-thumbnail-->

                    <?php   
                    }
                    ?>

                        <div class="entry-content">
                            <?php the_content(); ?>
                        </div><!-- .entry-content -->


                </article><!-- #post-## -->

                <?php
            }


            wp_reset_postdata();
        }
            echo $args['after_widget']; 

        if ( ! $this->is_preview() ) {
            $cache[ $args['widget_id'] ] = ob_get_flush();
            wp_cache_set( 'widget_new_posts', $cache, 'widget' );
        } else {
            ob_end_flush();
        }
    }

    public function update( $new_instance, $old_instance ) {
        $instance                   = $old_instance;
        $instance['title']          = strip_tags( $new_instance['title'] );
        $this->flush_widget_cache();

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

        return $instance;
    }

    public function flush_widget_cache() {
        wp_cache_delete('widget_new_posts', 'widget');
    }

    public function form( $instance ) {

        $title      = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : '';
        ?>

        <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>


    <?php
    }

}

add_action( 'widgets_init', function () {
    register_widget( 'Newest_Post_Widget' );
});