How To Create Custom WordPress Widget – Complete Guide
In this article, we will be creating an extended version of the ‘Recent Posts’ widget – ‘Custom WordPress Widget’.
To follow these instructions, you will need a working WordPress install on a local machine or a hosting server, and a text editor, like Notepad++.
The ‘Recent Posts’ widget is actually a part of WordPress installation and it comes in a very basic form – just post titles with hyperlinks.
Now, I will create a different version that features a post thumbnail, post title, and a very small excerpt with all of it styled to stand out. Disclaimer – don’t take any design advice from me????.
***CAUTION*** I advise you to create and test your widget on a local WordPress installation (or at least on a test site). You should also backup your site.
This is because mistakes can cause fatal errors and crash your site, thereby inconveniencing your users.
The custom WordPress widget directory
Any WordPress add-on, regardless of being a plugin or a widget, will need to have its own directory in the ‘plugins’ and at least one PHP file.
So, let’s start by creating a directory.
First things first – navigate to the ‘wp-content/plugins’ directory (via FTP) and create a folder. Feel free to name it anything you want. In our case, it will be ‘fixrunner-custom-widget’.
Read our guide on how to use FTP, to learn how to access your WP files.
After that, create a PHP file using Notepad ++ (or any code editor).
Name it whatever you want, just make sure it ends with the ‘.php’ extension. I named my plugin file ‘fixrunner-widget-plugin.php’.
Copy and paste this in your plugin file and we will edit it later on:
<?php
/*
Plugin Name: Fixrunner Custom Widget
Plugin URI: https://www.fixrunner.com/creating-a-custom-widget
Description: A custom widget. That’s it.
Version: 1.0
Author: Fixrunner
Author URI: https://www.fixrunner.com
License: GPL2
*/
class My_Widget extends WP_Widget {
// class constructor
public function __construct() {}
// output the widget content on the front-end
public function widget( $args, $instance ) {}
// output the option form field in admin Widgets screen
public function form( $instance ) {}
// save options
public function update( $new_instance, $old_instance ) {}
}
// Register the widget
function my_register_custom_widget() {
register_widget( ‘Fixrunner_Custom_Widget’ );
}
add_action( ‘widgets_init’, ‘my_register_custom_widget’ );
Additionally, if you wish to style your custom WordPress widget beforehand, rather than styling it through the ‘Additional CSS’ or theme stylesheet, you will need a CSS file as well.
So, create a CSS file. You guessed it – any name is good as long as it ends with ‘.css’ extension. Also, you will need to enqueue the stylesheet by using the following code (adjust the parts of the code to fit the name of your file):
wp_enqueue_style( ‘fr-style’, plugins_url( ‘fr-style.css’, __FILE__ ), ”, ‘1.0’ );
Now, let’s move on with constructing the widget. There are 5 essential parts of the widget:
- Header and hook
- __construct() – The Class Constructor
- widget() – The content of the widget displayed on the front-end
- form() – The backend form of the widget displayed in the ‘Widgets’ section
- update() – The part that defines how the widget and its options are updated
We will go through each of them one by one.
Header and hook
This can be considered as the custom WordPress widget wrapper since everything else goes in between.
The header of the widget is where we define the name, author, description, version, and license. This information is visible in the ‘Plugins’ section of the WordPress dashboard.
In the image below, you will see an example of the plugin/widget header.
That’s all about the header. Now to the hook.
The hook is located at the very bottom of the file (which you already added):
// Register the widget
function my_register_custom_widget() {
register_widget( ‘Fixrunner_Custom_Widget’ );
}
add_action( ‘widgets_init’, ‘my_register_custom_widget’ );
This registers the widget and allows it to be displayed and activated in the ‘Plugin’ section.
If you followed the instructions above correctly, you will see your widget appearing under the ‘Plugins’ section. It won’t have any functionality yet, but we’ll deal with that in the following sections.
The Class Constructor – __construct()
This is a part of the code that defines the unique widget ID, title, and options (which can vary). Also, here, we are defining the ID, name, and options ‘classname’, ‘description’, ‘customize_selective_refresh’.
The last one (customize_selective_refresh) allows for changes to be visible when using the ‘Customizer’ (Appearance >> Customize) without refreshing the page.
class Fixrunner_Custom_Widget extends WP_Widget {
// Main constructor
public function __construct() {
$widget_ops = array(
‘classname’ => ‘fixrunner_custom_widget’,
‘description’ => ‘A plugin for Fixrunner readers.’,
‘customize_selective_refresh’ => true,
);
parent::__construct( ‘fixrunner_custom_widget’, ‘Fixrunner Custom Widget’, $widget_ops );
}
The ‘Description’ part is different from the header description. This description is visible under Appearance >>Widgets:
The custom WordPress widget content using ‘widget()’ function
So, this is the actual frontend of the custom WordPress widget. What you write here will be displayed on your site.
Therefore, in our case, there’s a substantial bit of the code (for those unfamiliar with PHP), but I’ve put in the comments to explain what the lines are for.
We are not going to go into detail for every line of code you might need to Google some of them:
//defining the frontend part
public function widget( $args, $instance ) {
if ( ! isset( $args[‘widget_id’] ) ) {
$args[‘widget_id’] = $this->id;
}
//getting the title from the backend
$title = ( ! empty( $instance[‘title’] ) ) ? $instance[‘title’] : __( ‘Recent Posts’ );
$title = apply_filters( ‘widget_title’, $title, $instance, $this->id_base );
//getting the number of posts from the backend (the default is 5 if no value is set)
$number = ( ! empty( $instance[‘number’] ) ) ? absint( $instance[‘number’] ) : 5;
if ( ! $number ) {
$number = 5;
}
//limiting excertp length to 10 words
function custom_excerpt_length( $length ) {
return 10;
}
add_filter( ‘excerpt_length’, ‘custom_excerpt_length’, 999 );
//setting up the loop arguments
$args = array(
‘post_type’ => ‘post’,
‘post_status’ => ‘publish’,
‘posts_per_page’ => $number);
$query1 = new WP_Query($args); ?>
<div class=”container-fr”>
<?php /* printing the widget title, if set */
if ( $title ) {
echo ‘<h3 style=”text-align: center;”>’.$args[‘before_title’] . $title . $args[‘after_title’].'</h3>’;} ?>
<?php // The Loop
while ( $query1->have_posts() ) {
$query1->the_post();
$featured_img_url = get_the_post_thumbnail_url();
?>
<!– the widget HTML and PHP –>
<a href=”<?php echo get_permalink(); ?>” >
<div class=”fr-post-container”>
<div class=”fr-thumb” style=”background-image: url(‘<?php echo esc_url($featured_img_url);?>’);”>
</div>
<div class=”fr-text”>
<span style=”margin: 0 0 5px 0; background-color: white; text-align: center; color: #666; display: block;font-weight: bold;”>
<?php
/* truncating the title if it’s more than 20 characters */
$thetitle = get_the_title();
$getlength = strlen($thetitle);
$thelength = 20;
echo substr($thetitle, 0, $thelength);
if ($getlength > $thelength) echo “…”; ?>
</span>
<p><?php the_excerpt(); ?></p>
</div>
</div></a>
<?php } ?>
</div>
<?php
}
Custom WordPress widget – The admin form – form()
In short, this is the part where we define how the widget will be displayed on the backend, in the ‘Appearances -> Widgets’.
Generally, if you are making something very simple, you might just need to create a field where the ‘Title’ for the custom widget is set.
We will create both the ‘Title’ and ‘Number of posts’ field (which will dictate how many of the recent posts will be displayed). This number is actually used in the code above.
You will see a variable ‘$number’ which is actually the number we set in the backend. To sum up, this is the code you will need:
public function form( $instance ) {
$title = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : '';
$number = isset( $instance['number'] ) ? absint( $instance['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 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>
<?php
}
}
Updating the custom WordPress widget options – update()
So, this part is just what it says – it updates the widget options when you change them in the backend. Since I have 2 options in my widget, I will need to set 2 lines for updating them:
public function update( $new_instance, $old_instance ) {
$instance = $old_instance;
$instance['title'] = sanitize_text_field( $new_instance['title'] );
$instance['number'] = (int) $new_instance['number'];
}
So, if you have just one option (like widget title) you will need just the first two lines and the title line ( $instance[‘title’] = sanitize_text_field( $new_instance[‘title’] ); ).
Additionally, if you have more options, than you will need to specify a line for each of them.
Let’s not forget the CSS
So, if you created the CSS file as per instructions above, you can paste the following lines of CSS code to get the styling that I created:
.fr-post-container {width: 235px; padding: 5px; background-color: #eee;margin:auto; min-height: 85px;border-bottom: 1px solid #999; border-radius: 10px; margin-bottom: 5px;transition: all .2s ease-in-out; -webkit-box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.75);
-moz-box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.75);
box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.75);}
.fr-post-container:hover {background-color: #eee; transform: scale(1.1);}
.fr-thumb {float: left; width: 75px; height: 75px; background-size: cover; background-position: center center; display: inline-block; }
.fr-text {width: 150px; display: inline-block; font-size: 11px; padding-left:5px;font-family: ‘Times New Roman’;}
.fr-text p {text-align: justify; margin: 0;background-color: white;padding:5px 5px 0px 5px; color: #666;line-height:11px;}
.container-fr {padding-top:10px; padding-bottom:10px;margin-bottom:20px;}
In short,we will not go into detail what each line of code does. However, they are pretty straight forward and takes about 5 minutes to Google out.
Testing a custom WordPress widget
If you followed the instructions correctly (or exactly, provided you wanted to create an identical widget) you can go ahead and add the widget to your WordPress sidebar.
Go to ‘Appearance >>Widgets’ and add the ‘Fixrunner Custom Widget’ to your sidebar area.
The result should be a widget created to display a post thumbnail, WordPress post title, and the excerpt with each of the items growing in size when hovered over:
Also, coding the widgets requires some Object-Oriented Programming familiarity and at least some knowledge of the WordPress loop.
You should consult the WordPress Codex if you are in doubt, and you will be. There’s nothing wrong or odd about it.
At least, now you know how to create a plugin/widget directory and file in WordPress. If this sounds too complicated, you can reach out to us. We will fix any WordPress issue you have.
Thorough testing is essential to ensure your custom widget functions correctly and securely. Regularly scan your site for vulnerabilities; early detection can prevent serious issues that require WordPress Malware Removal services. Also, tools like Google PageSpeed Insights or GTmetrix can provide valuable feedback. Making necessary adjustments based on these insights will help you reduce WordPress page load time and ensure optimal performance.
More Resources:
- How to Add Expires Headers in WordPress
- Err_ssl_version_or_cipher_mismatch Error – How to fix it
- How To Fix Err_Too_Many_Redirects Error In WordPress