Widgets API
Framework for creating dynamic sidebars and registering widgets in WordPress.
Since: 2.2.0
Source: wp-includes/widgets.php, wp-includes/class-wp-widget.php, wp-includes/class-wp-widget-factory.php, wp-includes/widgets/
Components
| Component | Description |
|---|---|
| functions.md | Core widget and sidebar functions |
| class-wp-widget.md | Base widget class for extending |
| class-wp-widget-factory.md | Widget factory singleton |
| hooks.md | Actions and filters |
Architecture Overview
The Widgets API consists of three main concepts:
- Widgets – Discrete content blocks that can be placed in sidebars
- Sidebars (Widget Areas) – Named regions where widgets can be assigned
- Widget Factory – Singleton that manages widget registration
Global Variables
global $wp_registered_sidebars; // Registered sidebars
global $wp_registered_widgets; // Registered widget instances
global $wp_registered_widget_controls; // Widget control/form callbacks
global $wp_registered_widget_updates; // Widget update callbacks
global $wp_widget_factory; // WP_Widget_Factory singleton
Registration Flow
wp_widgets_init()
└── register_widget( 'My_Widget_Class' )
└── WP_Widget_Factory::register()
└── new My_Widget_Class()
'widgets_init' action
└── WP_Widget_Factory::_register_widgets()
└── WP_Widget::_register()
└── wp_register_sidebar_widget()
└── _register_widget_update_callback()
└── _register_widget_form_callback()
Sidebar Registration Flow
register_sidebar( $args )
└── Adds to $wp_registered_sidebars
└── add_theme_support( 'widgets' )
└── do_action( 'register_sidebar', $sidebar )
Widget Display Flow
dynamic_sidebar( $index )
├── do_action( 'dynamic_sidebar_before' )
├── foreach ( $sidebars_widgets[ $index ] as $widget_id )
│ ├── apply_filters( 'dynamic_sidebar_params' )
│ ├── do_action( 'dynamic_sidebar', $widget )
│ └── call_user_func_array( $callback, $params )
├── do_action( 'dynamic_sidebar_after' )
└── return apply_filters( 'dynamic_sidebar_has_widgets' )
Widget Instance Storage
Widget settings are stored in the options table:
- Option name:
widget_{$id_base}(e.g.,widget_text) - Multi-widget format with numeric keys for each instance
- Special key
_multiwidgetindicates multi-widget format
Sidebars-Widgets Mapping
Widget-to-sidebar assignments stored in sidebars_widgets option:
array(
'sidebar-1' => array( 'text-1', 'search-2' ),
'sidebar-2' => array( 'archives-1' ),
'wp_inactive_widgets' => array( 'calendar-1' ),
)
Default Widgets
WordPress ships with these core widgets:
| Class | ID Base | Description |
|---|---|---|
WP_Widget_Pages |
pages |
List of pages |
WP_Widget_Calendar |
calendar |
Calendar |
WP_Widget_Archives |
archives |
Monthly archives |
WP_Widget_Links |
links |
Blogroll links (if enabled) |
WP_Widget_Media_Audio |
media_audio |
Audio player |
WP_Widget_Media_Image |
media_image |
Single image |
WP_Widget_Media_Video |
media_video |
Video player |
WP_Widget_Media_Gallery |
media_gallery |
Image gallery |
WP_Widget_Meta |
meta |
Login/RSS/WordPress links |
WP_Widget_Search |
search |
Search form |
WP_Widget_Text |
text |
Arbitrary text/HTML |
WP_Widget_Categories |
categories |
Category list |
WP_Widget_Recent_Posts |
recent-posts |
Recent posts |
WP_Widget_Recent_Comments |
recent-comments |
Recent comments |
WP_Widget_RSS |
rss |
RSS feed entries |
WP_Widget_Tag_Cloud |
tag_cloud |
Tag cloud |
WP_Nav_Menu_Widget |
nav_menu |
Navigation menu |
WP_Widget_Custom_HTML |
custom_html |
Custom HTML code |
WP_Widget_Block |
block |
Gutenberg block container |
Block-Based Widgets (5.8+)
Since WordPress 5.8, the widgets screen uses the block editor by default:
// Check if block widgets are enabled
if ( wp_use_widgets_block_editor() ) {
// Block-based widget editor
}
// Disable block widgets (in theme)
remove_theme_support( 'widgets-block-editor' );
Creating a Custom Widget
class My_Widget extends WP_Widget {
public function __construct() {
parent::__construct(
'my_widget', // id_base
'My Widget', // name
array( // widget_options
'classname' => 'my_widget',
'description' => 'A custom widget.',
)
);
}
public function widget( $args, $instance ) {
echo $args['before_widget'];
if ( ! empty( $instance['title'] ) ) {
echo $args['before_title'] .
apply_filters( 'widget_title', $instance['title'] ) .
$args['after_title'];
}
// Widget content here
echo $args['after_widget'];
}
public function form( $instance ) {
$title = ! empty( $instance['title'] ) ? $instance['title'] : '';
?>
<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' ); ?>"
value="<?php echo esc_attr( $title ); ?>">
</p>
<?php
}
public function update( $new_instance, $old_instance ) {
$instance = array();
$instance['title'] = sanitize_text_field( $new_instance['title'] );
return $instance;
}
}
add_action( 'widgets_init', function() {
register_widget( 'My_Widget' );
});
Registering a Sidebar
add_action( 'widgets_init', function() {
register_sidebar( array(
'name' => 'Main Sidebar',
'id' => 'sidebar-1',
'description' => 'Widgets in this area appear on all posts and pages.',
'before_widget' => '<section id="%1$s" class="widget %2$s">',
'after_widget' => '</section>',
'before_title' => '<h2 class="widget-title">',
'after_title' => '</h2>',
) );
});
Displaying a Sidebar
// In theme template
if ( is_active_sidebar( 'sidebar-1' ) ) {
dynamic_sidebar( 'sidebar-1' );
}
Programmatic Widget Output
// Display a widget directly without sidebar
the_widget( 'WP_Widget_Recent_Posts', array(
'title' => 'Recent Posts',
'number' => 5,
), array(
'before_widget' => '<div class="widget">',
'after_widget' => '</div>',
'before_title' => '<h3>',
'after_title' => '</h3>',
) );