WordPress Plugin: How To Build A Custom Sidebar Widget For Your Author Bio

I recently moved my author bio into the sidebar to make it more visible to readers, especially on long-form articles where the author’s expertise matters. WordPress includes great built-in author fields, but there wasn’t a clean, flexible way to output them in a widget—at least not without writing custom markup every time or relying on a theme feature that might break later. I wanted something simple, extensible, and guaranteed to show only when a single post is displayed.
Author Bio Sidebar Widget
The following plugin solves that. It registers a lightweight sidebar widget that pulls the current post’s author details and publishes them using substitution strings. These strings let me design the layout however I want while keeping the logic safely abstracted. Even better, I can add new token replacements in the future—such as social profiles or custom fields—without having to recode the widget itself.
96,
)
);

$display_name = get_the_author_meta( ‘display_name’, $author_id );
$bio_raw = get_the_author_meta( ‘description’, $author_id );
$bio_clean = wp_kses_post( $bio_raw );
$bio = wpautop( $bio_clean );

$author_url = get_author_posts_url( $author_id );

$tokens = array(
‘{avatar}’ => get_avatar(
$author_id,
(int) $args[‘avatar_size’],
”,
$display_name,
array( ‘class’ => ‘author-bio-widget-avatar’ )
),
‘{display_name}’ => esc_html( $display_name ),
‘{bio}’ => $bio,
‘{author_url}’ => esc_url( $author_url ),
‘{author_link}’ => sprintf(
‘<a href=”%s">%s</a>’,
esc_url( $author_url ),
esc_html( $display_name )
),
‘{user_nicename}’ => esc_html( get_the_author_meta( ‘user_nicename’, $author_id ) ),
‘{user_email}’ => antispambot( get_the_author_meta( ‘user_email’, $author_id ) ),
);

/**
* Filter: dk_author_bio_widget_tokens
*
* Allows adding or modifying tokens without changing the widget code.
*
* Example:
* add_filter( ‘dk_author_bio_widget_tokens’, function( $tokens, $author_id, $args ) {
* $tokens[‘{twitter}’] = esc_html( get_the_author_meta( ‘twitter’, $author_id ) );
* return $tokens;
* }, 10, 3 );
*/
return apply_filters( ‘dk_author_bio_widget_tokens’, $tokens, $author_id, $args );
}

/**
* Author Bio Sidebar Widget.
*/
class DK_Author_Bio_Sidebar_Widget extends WP_Widget {

public function __construct() {
parent::__construct(
‘dk_author_bio_sidebar_widget’,
__( ‘Author Bio Sidebar’, ‘dk-author-bio-widget’ ),
array(
‘description’ => __( ‘Shows the current post author bio in the sidebar using substitution strings.’, ‘dk-author-bio-widget’ ),
)
);
}

/**
* Frontend display.
*/
public function widget( $args, $instance ) {
// Only show on single blog posts.
if ( ! is_singular( ‘post’ ) ) {
return;
}

global $post;

if ( ! $post instanceof WP_Post ) {
return;
}

$author_id = (int) $post->post_author;

if ( ! $author_id ) {
return;
}

$title = isset( $instance[‘title’] ) ? $instance[‘title’] : ”;
$template = isset( $instance[‘template’] ) ? $instance[‘template’] : ”;
$avatar_size = isset( $instance[‘avatar_size’] ) ? (int) $instance[‘avatar_size’] : 96;

if ( ” === trim( $template ) ) {
// Default template: avatar, linked name, and bio.
$template = <<<HTML
{avatar}
<h3 class="author-bio-widget-name">{author_link}</h3>
<div class="author-bio-widget-bio">
{bio}
</div>
HTML;
}

$tokens = dk_author_bio_widget_get_tokens(
$author_id,
array(
‘avatar_size’ => $avatar_size,
)
);
$content = strtr( $template, $tokens );

// Basic safety: allow only safe HTML in final output.
$content = wp_kses_post( $content );

echo $args[‘before_widget’];

if ( ! empty( $title ) ) {
echo $args[‘before_title’] . esc_html( $title ) . $args[‘after_title’];
}

echo ‘<div class="dk-author-bio-sidebar-widget">’;
echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo ‘</div>’;

echo $args[‘after_widget’];
}

/**
* Backend form.
*/
public function form( $instance ) {
$title = isset( $instance[‘title’] ) ? $instance[‘title’] : __( ‘About the Author’, ‘dk-author-bio-widget’ );
$template = isset( $instance[‘template’] ) ? $instance[‘template’] : ”;
$avatar_size = isset( $instance[‘avatar_size’] ) ? (int) $instance[‘avatar_size’] : 96;

$field_id_title = $this->get_field_id( ‘title’ );
$field_name_title = $this->get_field_name( ‘title’ );
$field_id_template = $this->get_field_id( ‘template’ );
$field_name_template = $this->get_field_name( ‘template’ );
$field_id_avatar_size = $this->get_field_id( ‘avatar_size’ );
$field_name_avatar = $this->get_field_name( ‘avatar_size’ );
?>
<p>
<label for="<?php echo esc_attr( $field_id_title ); ?>">
<?php esc_html_e( ‘Title:’, ‘dk-author-bio-widget’ ); ?>
</label>
<input class="widefat"
id="<?php echo esc_attr( $field_id_title ); ?>"
name="<?php echo esc_attr( $field_name_title ); ?>"
type="text"
value="<?php echo esc_attr( $title ); ?>" />
</p>

<p>
<label for="<?php echo esc_attr( $field_id_avatar_size ); ?>">
<?php esc_html_e( ‘Avatar size (px):’, ‘dk-author-bio-widget’ ); ?>
</label>
<input class="small-text"
id="<?php echo esc_attr( $field_id_avatar_size ); ?>"
name="<?php echo esc_attr( $field_name_avatar ); ?>"
type="number"
min="16"
value="<?php echo esc_attr( $avatar_size ); ?>" />
</p>

<p>
<label for="<?php echo esc_attr( $field_id_template ); ?>">
<?php esc_html_e( ‘Template:’, ‘dk-author-bio-widget’ ); ?>
</label>
<textarea class="widefat"
rows="10"
id="<?php echo esc_attr( $field_id_template ); ?>"
name="<?php echo esc_attr( $field_name_template ); ?>"><?php echo esc_textarea( $template ); ?></textarea>
</p>

<p>
<?php esc_html_e( ‘Available substitution strings:’, ‘dk-author-bio-widget’ ); ?><br />
<code>{avatar}</code> – <?php esc_html_e( ‘Author avatar image’, ‘dk-author-bio-widget’ ); ?><br />
<code>{display_name}</code> – <?php esc_html_e( ‘Author display name’, ‘dk-author-bio-widget’ ); ?><br />
<code>{bio}</code> – <?php esc_html_e( ‘Author bio (Biographical Info)’, ‘dk-author-bio-widget’ ); ?><br />
<code>{author_url}</code> – <?php esc_html_e( ‘URL to the author archive page’, ‘dk-author-bio-widget’ ); ?><br />
<code>{author_link}</code> – <?php esc_html_e( ‘Linked author name pointing to the author page’, ‘dk-author-bio-widget’ ); ?><br />
<code>{user_nicename}</code> – <?php esc_html_e( ‘User nicename (slug)’, ‘dk-author-bio-widget’ ); ?><br />
<code>{user_email}</code> – <?php esc_html_e( ‘Author email (obfuscated)’, ‘dk-author-bio-widget’ ); ?><br />
</p>

<p>
<?php esc_html_e( ‘Developers can add more tokens with the dk_author_bio_widget_tokens filter.’, ‘dk-author-bio-widget’ ); ?>
</p>
<?php
}

/**
* Sanitize and save widget options.
*/
public function update( $new_instance, $old_instance ) {
$instance = array();
$instance[‘title’] = isset( $new_instance[‘title’] ) ? sanitize_text_field( $new_instance[‘title’] ) : ”;
$instance[‘avatar_size’] = isset( $new_instance[‘avatar_size’] ) ? (int) $new_instance[‘avatar_size’] : 96;
$instance[‘template’] = isset( $new_instance[‘template’] ) ? $new_instance[‘template’] : ”;

return $instance;
}
}

/**
* Register the widget.
*/
function dk_register_author_bio_sidebar_widget() {
register_widget( ‘DK_Author_Bio_Sidebar_Widget’ );
}
add_action( ‘widgets_init’, ‘dk_register_author_bio_sidebar_widget’ );
How to Install Your WordPress Author Sidebar Widget

Create the plugin file. Copy the code above into a new file named author-bio-sidebar-widget.php.
Upload it to WordPress. Place the file in the wp-content/plugins/author-bio-sidebar-widget folder in your site.
Activate the plugin. After uploading it, go to the Plugins screen in the WordPress admin and activate it as you would any standard plugin. There’s nothing extra to configure behind the scenes.

After activation, the widget becomes available under Appearance → Widgets, where it behaves like any native WordPress widget. Drag it into the sidebar that appears on your single post templates. Because the widget is coded to display only on single posts, you don’t have to worry about it showing up on archive pages, the home page, or custom post types—it quietly stays hidden everywhere else.

The widget interface itself is intentionally minimal. It provides a title field, an optional avatar size, and a template box that lets me shape the output however I like. The real power is in the substitution strings:
{avatar}
<h3 class="author-bio-widget-name">{author_link}</h3>
<div class="author-bio-widget-bio">
{bio}
</div>

{avatar} Displays the author’s avatar image at the size defined in the widget settings.
{display_name} Outputs the author’s public display name.
{bio} Inserts the author’s biographical description from their WordPress profile, with proper formatting applied.
{author_url} Prints the URL to the author’s archive page, allowing you to build your own linked markup if needed.
{author_link} Automatically outputs the author’s display name as a clickable link to their author archive page.
{user_nicename} Includes the author’s nicename (the URL-friendly slug used in their archive link).
{user_email} Outputs the author’s email address in an obfuscated format using WordPress’s antispambot() function. Tokens such as {avatar}, {display_name}, {bio}, {author_link}, and {author_url} are automatically replaced with the corresponding author data. If I want to customize the layout or introduce HTML structure—headings, paragraphs, wrappers, or even utility classes—I can, and the widget dynamically fills in everything.

What I appreciate most is that additional fields can be introduced later through a simple filter. If I add social profile fields or custom user meta, I can register new tokens without touching the widget’s core code. This makes the widget future-proof and ensures the author bio always reflects the information I want to highlight.
Once the widget is placed and the template is set, the author bio automatically updates for every post to reflect whoever wrote it. The result is a consistent, prominent bio placement that strengthens trust and visibility across the entire site.
©2025 DK New Media, LLC, All rights reserved | DisclosureOriginally Published on Martech Zone: WordPress Plugin: How To Build A Custom Sidebar Widget For Your Author Bio

Scroll to Top