Creating a Custom Recent Posts Widget
Basic Approach
To create a custom recent posts widget, we'll need to:
- Create a class that extends
WP_Widget
- Register the widget with WordPress
- Define the widget's front-end display
- Add customization options in the admin area
Here's a step-by-step solution:
First, let's create the basic widget structure:
class Custom_Recent_Posts_Widget extends WP_Widget {
public function __construct() {
parent::__construct(
'custom_recent_posts',
'Custom Recent Posts',
array('description' => 'Displays recent posts with custom styling')
);
}
}
Add the widget registration in your theme's functions.php
:
function register_custom_recent_posts_widget() {
register_widget('Custom_Recent_Posts_Widget');
}
add_action('widgets_init', 'register_custom_recent_posts_widget');
Here's the complete widget implementation with styling options:
class Custom_Recent_Posts_Widget extends WP_Widget {
public function __construct() {
parent::__construct(
'custom_recent_posts',
'Custom Recent Posts',
array('description' => 'Displays recent posts with custom styling')
);
}
public function widget($args, $instance) {
$title = apply_filters('widget_title', $instance['title']);
$posts_number = (!empty($instance['number'])) ? absint($instance['number']) : 5;
$show_date = isset($instance['show_date']) ? $instance['show_date'] : false;
echo $args['before_widget'];
if (!empty($title)) {
echo $args['before_title'] . $title . $args['after_title'];
}
$recent_posts = new WP_Query(array(
'posts_per_page' => $posts_number,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC'
));
if ($recent_posts->have_posts()) :
echo '<ul class="custom-recent-posts">';
while ($recent_posts->have_posts()) : $recent_posts->the_post();
echo '<li>';
echo '<a href="' . esc_url(get_permalink()) . '">' . esc_html(get_the_title()) . '</a>';
if ($show_date) {
echo '<span class="post-date">' . get_the_date() . '</span>';
}
echo '</li>';
endwhile;
echo '</ul>';
endif;
wp_reset_postdata();
echo $args['after_widget'];
}
public function form($instance) {
$title = isset($instance['title']) ? $instance['title'] : 'Recent Posts';
$number = isset($instance['number']) ? absint($instance['number']) : 5;
$show_date = isset($instance['show_date']) ? (bool) $instance['show_date'] : false;
?>
<p>
<label for="<?php echo $this->get_field_id('title'); ?>">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 esc_attr($title); ?>">
</p>
<p>
<label for="<?php echo $this->get_field_id('number'); ?>">Number of posts:</label>
<input class="tiny-text" id="<?php echo $this->get_field_id('number'); ?>"
name="<?php echo $this->get_field_name('number'); ?>" type="number"
step="1" min="1" value="<?php echo $number; ?>" size="3">
</p>
<p>
<input class="checkbox" type="checkbox" <?php checked($show_date); ?>
id="<?php echo $this->get_field_id('show_date'); ?>"
name="<?php echo $this->get_field_name('show_date'); ?>">
<label for="<?php echo $this->get_field_id('show_date'); ?>">Display post date?</label>
</p>
<?php
}
public function update($new_instance, $old_instance) {
$instance = array();
$instance['title'] = sanitize_text_field($new_instance['title']);
$instance['number'] = (int) $new_instance['number'];
$instance['show_date'] = isset($new_instance['show_date']) ? (bool) $new_instance['show_date'] : false;
return $instance;
}
}
Styling the Widget
Add this CSS to your theme's stylesheet:
.custom-recent-posts {
list-style: none;
padding: 0;
margin: 0;
}
.custom-recent-posts li {
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.custom-recent-posts .post-date {
display: block;
font-size: 0.8em;
color: #666;
}
Security Considerations
- Always sanitize user input using
sanitize_text_field()
- Escape output with
esc_html()
, esc_attr()
, and esc_url()
- Use
wp_reset_postdata()
after custom queries
- Validate numerical inputs with
absint()
Common Pitfalls to Avoid
- Don't forget to reset post data after custom queries
- Avoid heavy queries that might slow down the site
- Remember to handle empty states
- Don't hardcode HTML without proper escaping
Alternative Solutions
If you prefer not to code, these plugins offer similar functionality:
-
Display Posts - Flexible shortcode-based solution
-
Recent Posts Widget Extended - Enhanced recent posts widget with many options
Best Practices
- Use WordPress coding standards
- Keep the widget lightweight
- Cache results for better performance
- Follow widget naming conventions
- Use meaningful variable names
- Include proper documentation
Remember to test the widget thoroughly in different themes and with various WordPress settings before deploying to production.