# Featured Post Widget

> Adds a "Featured Post" widget that highlights one post or page you pick, shown as a card with its featured image, excerpt and a read-more link.

- Type: PHP
- Runs: Run everywhere
- Category: [Widgets](https://wpide.com/library/category/widgets/)
- Tags: widgets, featured, cards
- Author: WPIDE Team
- Installs: 0
- Updated: 2026-10-28
- Page: https://wpide.com/library/featured-post-widget/

## Code

```php
if ( ! class_exists( 'WPIDE_Featured_Post_Widget' ) ) {
    class WPIDE_Featured_Post_Widget extends WP_Widget {
        public function __construct() {
            parent::__construct( 'wpide_featured_post', 'Featured Post', array(
                'description'                 => 'One post or page you choose, shown as a card with its image and excerpt.',
                'customize_selective_refresh' => true,
            ) );
        }

        public function widget( $args, $instance ) {
            $post = ! empty( $instance['post_id'] ) ? get_post( absint( $instance['post_id'] ) ) : null;

            if ( ! $post || 'publish' !== $post->post_status || post_password_required( $post ) || ( is_singular() && get_queried_object_id() === $post->ID ) ) {
                return;
            }

            $title = isset( $instance['title'] ) ? $instance['title'] : '';
            $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
            $link  = get_permalink( $post );

            echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput -- theme markup.

            if ( '' !== (string) $title ) {
                echo $args['before_title'] . esc_html( $title ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput
            }

            echo '<article class="wpide-featured-post">';

            if ( has_post_thumbnail( $post ) ) {
                echo '<a href="' . esc_url( $link ) . '" tabindex="-1" aria-hidden="true">' . get_the_post_thumbnail( $post, 'medium_large', array( 'alt' => '', 'style' => 'display:block;width:100%;height:auto;border-radius:6px;margin-bottom:.5em;' ) ) . '</a>';
            }

            echo '<h3 style="margin:0 0 .35em;font-size:1.1em;"><a href="' . esc_url( $link ) . '">' . esc_html( get_the_title( $post ) ) . '</a></h3>';
            echo '<p style="margin:0 0 .5em;">' . esc_html( wp_trim_words( get_the_excerpt( $post ), 25 ) ) . '</p>';
            echo '<a href="' . esc_url( $link ) . '">' . esc_html( ! empty( $instance['more'] ) ? $instance['more'] : 'Read more' ) . '<span class="screen-reader-text">: ' . esc_html( get_the_title( $post ) ) . '</span></a>';
            echo '</article>';
            echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput
        }

        public function form( $instance ) {
            $title   = isset( $instance['title'] ) ? $instance['title'] : 'Featured';
            $post_id = ! empty( $instance['post_id'] ) ? absint( $instance['post_id'] ) : 0;
            $more    = isset( $instance['more'] ) ? $instance['more'] : 'Read more';
            $choices = get_posts( array(
                'post_type'      => array( 'post', 'page' ),
                'posts_per_page' => 100,
                'no_found_rows'  => true,
                'orderby'        => 'date',
            ) );

            echo '<p><label for="' . esc_attr( $this->get_field_id( 'title' ) ) . '">Title:</label><input class="widefat" type="text" id="' . esc_attr( $this->get_field_id( 'title' ) ) . '" name="' . esc_attr( $this->get_field_name( 'title' ) ) . '" value="' . esc_attr( $title ) . '" /></p>';
            echo '<p><label for="' . esc_attr( $this->get_field_id( 'post_id' ) ) . '">Post or page:</label><select class="widefat" id="' . esc_attr( $this->get_field_id( 'post_id' ) ) . '" name="' . esc_attr( $this->get_field_name( 'post_id' ) ) . '"><option value="0">— Select —</option>';

            foreach ( $choices as $choice ) {
                echo '<option value="' . esc_attr( $choice->ID ) . '"' . selected( $post_id, $choice->ID, false ) . '>' . esc_html( get_the_title( $choice ) . ' (' . $choice->post_type . ')' ) . '</option>';
            }

            echo '</select></p>';
            echo '<p><label for="' . esc_attr( $this->get_field_id( 'more' ) ) . '">Link text:</label><input class="widefat" type="text" id="' . esc_attr( $this->get_field_id( 'more' ) ) . '" name="' . esc_attr( $this->get_field_name( 'more' ) ) . '" value="' . esc_attr( $more ) . '" /></p>';

            return '';
        }

        public function update( $new_instance, $old_instance ) {
            return array(
                'title'   => isset( $new_instance['title'] ) ? sanitize_text_field( $new_instance['title'] ) : '',
                'post_id' => isset( $new_instance['post_id'] ) ? absint( $new_instance['post_id'] ) : 0,
                'more'    => isset( $new_instance['more'] ) ? sanitize_text_field( $new_instance['more'] ) : '',
            );
        }
    }
}

add_action( 'widgets_init', function () {
    register_widget( 'WPIDE_Featured_Post_Widget' );
} );
```

## Notes

The widget hides itself on the featured post's own page and when the post is unpublished.

## How to use it

Install the free WPIDE plugin (https://wordpress.org/plugins/wpide/), find "Featured Post Widget" in its snippet library and add it in one click; or create a new PHP snippet in WPIDE (or any code snippets plugin) and paste the code above. More snippets: https://wpide.com/library/
