SponsoredHosinger Special Offer

WP WordPress Theme Development

Build classic and block WordPress themes from scratch: the template hierarchy, The Loop, the_title() and the_content(), template parts, menus, the Customizer, child themes and theme.json.

Sidebars and Widget Areas

WordPress Theme Development Lesson 14 of 30 ~3 min read

Overview

A widget area — still called a "sidebar" in code even when it sits in a footer — is a slot you register with register_sidebar() and print with dynamic_sidebar(), which the site owner fills with blocks or widgets from Appearance → Widgets.

Registering widget areas

<?php
function gdc_starter_widgets_init() {
    register_sidebar(
        array(
            'name'          => esc_html__( 'Blog Sidebar', 'gdc-starter' ),
            'id'            => 'sidebar-1',
            'description'   => esc_html__( 'Shown beside blog posts.', 'gdc-starter' ),
            'before_widget' => '<section id="%1$s" class="widget %2$s">',
            'after_widget'  => '</section>',
            'before_title'  => '<h2 class="widget-title">',
            'after_title'   => '</h2>',
        )
    );

    // Three footer columns in one loop.
    for ( $i = 1; $i <= 3; $i++ ) {
        register_sidebar(
            array(
                /* translators: %d: footer column number */
                'name'          => sprintf( esc_html__( 'Footer Column %d', 'gdc-starter' ), $i ),
                'id'            => 'footer-' . $i,
                'before_widget' => '<div id="%1$s" class="widget %2$s">',
                'after_widget'  => '</div>',
                'before_title'  => '<h3 class="widget-title">',
                'after_title'   => '</h3>',
            )
        );
    }
}
add_action( 'widgets_init', 'gdc_starter_widgets_init' );

In before_widget, WordPress replaces %1$s with the widget's unique ID and %2$s with its class name, so every widget can be targeted in CSS. The id you choose — sidebar-1 — is what you print later. Keep it lowercase and never change it once the theme is in use, or the owner's widgets disappear.

sidebar.php

<?php
/**
 * The sidebar containing the main widget area.
 */
if ( ! is_active_sidebar( 'sidebar-1' ) ) {
    return;
}
?>

<aside id="secondary" class="widget-area" aria-label="<?php esc_attr_e( 'Sidebar', 'gdc-starter' ); ?>">
    <?php dynamic_sidebar( 'sidebar-1' ); ?>
</aside>

The early return is the important line. is_active_sidebar() is false when the area has no widgets, so the empty <aside> is never printed and your layout can switch to full width.

Including the sidebar

<?php get_header(); ?>

<div class="layout <?php echo is_active_sidebar( 'sidebar-1' ) ? 'has-sidebar' : 'no-sidebar'; ?>">
    <main class="site-main">
        <?php /* The Loop */ ?>
    </main>

    <?php get_sidebar(); ?>
</div>

<?php get_footer(); ?>

get_sidebar() loads sidebar.php. get_sidebar( 'shop' ) would load sidebar-shop.php.

Block widgets

Since WordPress 5.8, Appearance → Widgets uses the block editor, so owners can place any block — a paragraph, an image, a list of latest posts — in your widget area. Your register_sidebar() code is unchanged. Do test with blocks as well as classic widgets: blocks do not use before_title, so a heading block appears exactly as the owner set it.

Practice

  • Register sidebar-1 and three footer columns.
  • Create sidebar.php with the is_active_sidebar() guard and include it in single.php.
  • Remove every widget and confirm the layout switches to full width with no empty <aside> in the source.
Create a free account to save which lessons you've finished. Save my progress