riksi Start a project

Dev log 05 DevWordPress

Create and use a dynamic sidebar (widget area) in WordPress

Updated 4 min read By

You want site owners to add their own widgets to your theme, in the footer or beside the content. Register the widget area with register_sidebar() on the widgets_init hook. Then print it in your theme with dynamic_sidebar( 'your-id' ), inside an is_active_sidebar() check. That way an empty area leaves no stray markup.

A widget area is a place where site owners add widgets. WordPress calls it a sidebar, but it can go in the header, footer, beside the content or inside it.

Register the widget area

<?php
// functions.php
add_action( 'widgets_init', function () {
	register_sidebar( [
		'name'          => __( 'Footer column', 'riksi' ),
		'id'            => 'footer-1',
		'description'   => __( 'Shown in the first footer column on every page.', 'riksi' ),
		'before_widget' => '<section id="%1$s" class="widget %2$s">',
		'after_widget'  => '</section>',
		'before_title'  => '<h2 class="widget-title">',
		'after_title'   => '</h2>',
	] );
} );

A few details save a lot of confusion later. I get these right on every new theme, because they’re hard to change once widgets are in place.

  • Always set an id, in lowercase. Without one, WordPress numbers the areas (sidebar-1, sidebar-2) in the order you register them. If you then add or reorder areas, widgets move to the wrong place. The widgets don’t get lost. They turn up in whichever area now has their old number. Since WordPress 4.2, a missing ID also raises a notice.
  • Keep IDs to lowercase letters, numbers, hyphens and underscores. dynamic_sidebar() runs the value you pass through sanitize_title() before it looks it up. So an ID like Sidebar_Name never matches.
  • Set the wrappers. The default before_widget is an <li>, which is invalid HTML unless you print the area inside a list. I’d use a <section> or <div> instead.

Show it in the theme

Put this wherever the area belongs, for example in footer.php.

<?php if ( is_active_sidebar( 'footer-1' ) ) : ?>
	<aside class="footer-widgets" aria-label="<?php esc_attr_e( 'Footer', 'riksi' ); ?>">
		<?php dynamic_sidebar( 'footer-1' ); ?>
	</aside>
<?php endif; ?>

is_active_sidebar() returns true only when the area has widgets. So when the area is empty, the wrapper and any layout that depends on it disappear. You can also use it to change the layout, such as one column when the side area is empty. I wrap every widget area in this check.

dynamic_sidebar() prints the widgets and returns true if it found any.

A labelled <aside> gives screen reader users a landmark, which is a section of the page they can jump to. It works like the list of chapters at the front of a book. Pick a before_title heading level that fits the page outline. h2 suits most footers and side columns.

The area now shows up under Appearance > Widgets, with the name and description you gave it. This is how it looks on the classic Widgets screen.

A newly registered widget area listed on the classic WordPress Widgets screen

The register_sidebar() arguments

It’s a long list for one box of widgets. You’ll set a few of them on every theme, and the rest only now and then.

Argument What it controls Default
name Label on the Widgets screen. Sidebar N
id Key for dynamic_sidebar() and is_active_sidebar(). sidebar-N
description Help text on the Widgets screen. Empty
class Extra CSS class on the area in the admin Widgets screen (not the front end). Empty
before_widget, after_widget HTML around each widget. %1$s is the widget’s ID and %2$s is its class names. <li> list item
before_title, after_title HTML around a classic widget’s title. <h2 class="widgettitle">
before_sidebar, after_sidebar HTML around the whole area, printed only when it has widgets (WordPress 5.6+). Empty
show_in_rest Whether the area is public in the REST API (WordPress 5.9+). false, admins only

before_sidebar and after_sidebar can replace the is_active_sidebar() wrapper in simple cases. WordPress skips them when the area is empty.

Block widgets since WordPress 5.8

Since WordPress 5.8, the Widgets screen and the Customizer use the block editor. That includes classic themes. Each block you add to an area is stored as a widget. Your before_widget and after_widget wrap each top-level block.

Two things change for theme developers.

  • before_title and after_title don’t apply to blocks. A heading is only a Heading block, at whatever level the editor picks. So style .widget h2, .widget h3 and so on, not only .widget-title. I do this on every classic theme.
  • Classic widgets still work. They appear inside a Legacy Widget block and keep using your title wrappers.

If a site needs the old screen back, for example because the site owner knows it well, turn off block widgets in the theme. I’d only do this when there’s a clear reason for it.

<?php
add_action( 'after_setup_theme', function () {
	remove_theme_support( 'widgets-block-editor' );
} );

Block themes work differently. They normally register no widget areas, so there’s no Widgets screen. Headers, footers and sidebars are template parts that you edit in the Site Editor. Everything in this post applies to classic (PHP) themes.

Before you hand a site over, I’d test each widget area with a block and with a legacy widget. It only takes a few minutes, and it shows you whether the headings and wrappers look right in both. Site owners can add either kind. It’s nicer if you see the result before they do.

Filed under DevWordPress
Tagged
Share:

Comments

No comments yet. Questions, fixes and better ways are all welcome.

Leave a comment

Your email is never shown. Comments are checked before they appear, so yours may take a little while.

Start a project

Tell us what is
not working.

A few lines is enough. A real person reads every message and replies by email. Or choose the way that suits you.