Widgets are one of the oldest and most flexible ways to add functionality to your WordPress site. You can build one entirely from scratch — no plugins needed. In this guide, you’ll create a simple custom widget that you can drop into any sidebar or widget area using just PHP.
Step 1: Define Your Widget Class
Widgets in WordPress are classes that extend the built-in `WP_Widget` class. Each widget is a small self-contained component with its own display logic, admin form, and settings. Let’s start by defining a new widget class in your theme’s `functions.php` file:
1class My_Custom_Widget extends WP_Widget {
2
3 public function __construct() {
4 // Initialize the widget
5 }
6
7 public function widget( $args, $instance ) {
8 // Display widget output
9 }
10
11 public function form( $instance ) {
12 // Admin form
13 }
14
15 public function update( $new_instance, $old_instance ) {
16 // Save widget settings
17 }
18 }This sets up the structure we’ll fill out next. Each method handles a different part of the widget lifecycle — from output to saving settings.
Step 2: Flesh Out the Widget Class
The Constructor
1public function __construct() {
2 $widget_options = array(
3 'classname' => 'my_custom_widget',
4 'description' => 'A simple custom widget example',
5 );
6 parent::__construct( 'my_custom_widget', 'My Custom Widget', $widget_options );
7 }The constructor gives your widget a name and description. It also registers it with WordPress so it appears in the Widgets screen or Block Widgets interface.
The Widget Output
1public function widget( $args, $instance ) {
2 $title = apply_filters( 'widget_title', $instance['title'] ?? '' );
3
4 echo $args['before_widget'];
5 if ( ! empty( $title ) ) {
6 echo $args['before_title'] . esc_html( $title ) . $args['after_title'];
7 }
8
9 // Widget content goes here
10 echo '<p>Hello from my custom widget!</p>';
11
12 echo $args['after_widget'];
13 }This is what your widget outputs on the front end. You can replace the placeholder message with anything you want — text, markup, or dynamic content.
The Admin Form
1public function form( $instance ) {
2 $title = ! empty( $instance['title'] ) ? $instance['title'] : '';
3 ?>
4 <p>
5 <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">Title:</label>
6 <input
7 type="text"
8 id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
9 name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
10 value="<?php echo esc_attr( $title ); ?>"
11 style="width:100%;"
12 />
13 </p>
14 <?php
15 }This defines the form fields users see when they configure your widget in the admin panel. Here we’ve added a single text input for the widget title.
The Update Method
public function update( $new_instance, $old_instance ) {
$instance = array();
$instance['title'] = sanitize_text_field( $new_instance['title'] );
return $instance;
}This method saves widget settings when the user updates the widget. Always sanitize inputs to keep things secure.
Step 3: Register the Widget
Finally, you need to register your widget with WordPress. Add this to your `functions.php` file:
function register_my_custom_widget() {
register_widget( 'My_Custom_Widget' );
}
add_action( 'widgets_init', 'register_my_custom_widget' );That’s it — your new widget is now available in **Appearance → Widgets**. You can drag it into any widget area, give it a title, and it will render your custom content on the front end.
Full Example
1class My_Custom_Widget extends WP_Widget {
2
3 public function __construct() {
4 $widget_options = array(
5 'classname' => 'my_custom_widget',
6 'description' => 'A simple custom widget example',
7 );
8 parent::__construct( 'my_custom_widget', 'My Custom Widget', $widget_options );
9 }
10
11 public function widget( $args, $instance ) {
12 $title = apply_filters( 'widget_title', $instance['title'] ?? '' );
13 echo $args['before_widget'];
14 if ( ! empty( $title ) ) {
15 echo $args['before_title'] . esc_html( $title ) . $args['after_title'];
16 }
17 echo '<p>Hello from my custom widget!</p>';
18 echo $args['after_widget'];
19 }
20
21 public function form( $instance ) {
22 $title = ! empty( $instance['title'] ) ? $instance['title'] : '';
23 ?>
24 <p>
25 <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">Title:</label>
26 <input type="text" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" value="<?php echo esc_attr( $title ); ?>" />
27 </p>
28 <?php
29 }
30
31 public function update( $new_instance, $old_instance ) {
32 $instance = array();
33 $instance['title'] = sanitize_text_field( $new_instance['title'] );
34 return $instance;
35 }
36 }
37
38 function register_my_custom_widget() {
39 register_widget( 'My_Custom_Widget' );
40 }
41 add_action( 'widgets_init', 'register_my_custom_widget' );Useful Links
Official WordPress Widget API documentation:
Reference for WP_Widget class:
And that’s all it takes — you’ve just built your first custom WordPress widget from scratch. From here, you can add more settings, integrate APIs, or output dynamic content. Once you get comfortable with this pattern, the possibilities are wide open.

I’m an experienced SEO professional with over a decade of helping over 100 businesses rank higher online, especially local businesses, e-commerce stores and SaaS. As the co-founder of LPagery, I specialize in practical, proven strategies for regular SEO and Local SEO success.