How to create unique widget areas for individual posts in WordPress

Here’s a cool little trick for adding widget areas that are unique to posts, but without cluttering up Appearance->Widgets in the admin. Instead, we utilize the Customizer, which is perfect for this sort of thing.

First thing, we need to hook a function into `init` where we register our widget area.

add_action( 'init', 'my_namespaced_widgets_area' );

function my_namespaced_widgets_area() {
	// Our code goes here.
}

We are using `init` rather than `widgets_init` here because `widgets_init` fires in too high of a priority I found for this to work, and `init` is just fine for our needs. Anyway, let’s start writing this function.

function my_namespaced_widgets_area() {
	if ( ! isset( $_GET['url'] ) ) {
		$url = home_url( $_SERVER['REQUEST_URI'] );
	} else {
		$url = $_GET['url'];
	}
	if ( $post_id = url_to_postid( esc_url_raw( $url ) ) ) {
		// More to come...
	}
}

So this first bit of the function either grabs the query string stored in `$_GET[‘url’]`, or the URL itself. The former we use for registering the widget area with the customizer, as the URL of the post is passed as a query string; the latter we use for the actual post itself. The reason we are using the URL is because we are very early in the page load, and are limited in ways of obtaining the `$post_id` or `$post`.

Now some of you might notice I’m using `url_to_postid()` function to obtain the `$post_id`. If you’re on a small site, this is probably fine. However, `url_to_postid()` is an uncached function and should be avoided if you are working on a larger site where performance could be greatly effected. If you have access to WordPress.com functions, you can use `wpcom_vip_url_to_postid()` to do the same thing (but cached), or you can find another clever way to get the `$post_id` (or the `$post`), as this is the objective.

Now that we have the `$post`, we have everything we need to register our widget area. This is the complete code that you can place into `functions.php`.

add_action( 'init', 'my_namespaced_widgets_area' );

function my_namespaced_widgets_area() {
	if ( ! isset( $_GET['url'] ) ) {
		$url = home_url( $_SERVER['REQUEST_URI'] );
	} else {
		$url = $_GET['url'];
	}
	if ( $post_id = url_to_postid( esc_url_raw( $url ) ) ) {
		$post = get_post( $post_id );
		if ( $post ) {
			register_sidebar( array (
				'name'          => $post->post_title,
				'id'            => $post->post_name . '-widget-area',
				'description'   => $post->post_title . ' widget area',
				'before_widget' => '<li id="%1$s" class="widget %2$s">',
				'after_widget'  => '</li>',
				'before_title'  => '<h2 class="widgettitle">',
				'after_title'   => '</h2>' 
			);
		}
	}
}

Here we are using the `post_title` and `post_name` from the `$post` object. We use `post_title` for the “nice name” of naming the widget area and the `post_name` for the widget area’s ID. This now registers this widget area on both the customizer view and the single post view.

One last thing before we move on, you can add an `&&` to the `if ( $post )` statement and also check `$post->post_type` if you only want to register your widget area on a particular post type (post, page, or a custom post type).

Now that our widget area is registered, we need to add it to a template to render. So within `single.php` or the like, you can add in your new widget area like so:

$widget_area = $post->post_name . '-widget-area';
if ( is_active_sidebar( $widget_area ) ) {
	dynamic_sidebar( $widget_area );
}

From the URL of the post, we registered a widget area specifically for this post. Now, if you are logged in, click on “Customize” in the admin bar. Under “Widgets,” you should see your widget area made specifically for this post, and you can now add some widgets to it.

Another cool thing you can do is have a global fallback widget area. To do that, let’s say we register a widget area called `sidebar-2` globally. We can then use `is_active_sidebar` with an added `else`:

$widget_area = $post->post_name . '-widget-area';
if ( is_active_sidebar( $widget_area ) ) {
	dynamic_sidebar( $widget_area );
} else {
	dynamic_sidebar( 'sidebar-2' );
}

Now if we have added widgets to this post, they will display. If we haven’t, the widgets in `sidebar-2` will display.

Hope you found this post useful. Please let me know in the comments if you have any feedback or interesting ways to improve this code.

,

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *