Dashboard Widget Generator

Generate a WordPress dashboard widget: the registration, a capability check, the content callback and an optional settings form with its own nonce.

Live output

Enable JavaScript to customise; default output below.

Also the key users' hidden and ordered dashboard preferences are stored against.

Content

Each option is a worked example you can replace. The wiring around it stays the same.

Live preview dashboard-widget.php
<?php
/**
 * The "Site Overview" dashboard widget.
 */

/**
 * Registers the widget.
 */
function my_plugin_add_dashboard_widget() {
	// The dashboard is open to every logged-in user, subscribers included.
	if ( ! current_user_can( 'edit_posts' ) ) {
		return;
	}

	wp_add_dashboard_widget(
		'my_plugin_overview',
		__( 'Site Overview', 'my-plugin' ),
		'my_plugin_dashboard_widget',
		null
	);
}
add_action( 'wp_dashboard_setup', 'my_plugin_add_dashboard_widget' );

/**
 * Prints the widget.
 */
function my_plugin_dashboard_widget() {
	$count = 5;

	echo '<p>' . esc_html__( 'Everything that changed this week.', 'my-plugin' ) . '</p>';

	$recent = get_posts(
		array(
			'post_type'      => 'post',
			'posts_per_page' => $count,
			'post_status'    => 'publish',
			// The dashboard runs on every admin page load, so keep it cheap.
			'no_found_rows'  => true,
		)
	);

	if ( ! $recent ) {
		echo '<p>' . esc_html__( 'Nothing published yet.', 'my-plugin' ) . '</p>';

		return;
	}

	echo '<ul>';

	foreach ( $recent as $item ) {
		printf(
			'<li><a href="%1$s">%2$s</a> <span class="description">%3$s</span></li>',
			esc_url( get_edit_post_link( $item->ID ) ),
			esc_html( get_the_title( $item ) ),
			esc_html( get_the_date( '', $item ) )
		);
	}

	echo '</ul>';
}

Output is valid and updates as you type.

Generate a dashboard widget with the parts that are easy to leave out: the capability check, a cheap query, and a Configure form whose save runs in the right place.

How to use

  1. Set the capability. Every logged-in user can open the dashboard, so a widget with no check is visible to subscribers on a membership site.
  2. Keep the query small. The dashboard loads on every admin page visit for some users, so posts_per_page and no_found_rows matter more here than on the front end.
  3. Add the settings form only if there is something worth configuring. It brings a nonce, a save and an option with it.
  4. Leave the position at normal. Forcing a widget to the top overrides the arrangement each user chose for themselves.
  5. Paste the file into your plugin. Both the widget and its settings hook themselves up.

Example

The registration, with the check that belongs in front of it:

function acme_add_dashboard_widget() {
	if ( ! current_user_can( 'edit_posts' ) ) {
		return;
	}

	wp_add_dashboard_widget(
		'acme_overview',
		__( 'Site Overview', 'acme' ),
		'acme_dashboard_widget',
		null
	);
}
add_action( 'wp_dashboard_setup', 'acme_add_dashboard_widget' );

The fourth argument is the settings callback. Passing null means no Configure link appears.

Pitfalls

  • wp_dashboard_setup runs for everyone who loads the dashboard. Without a capability check the widget renders for subscribers.
  • The settings callback is called both to print the form and to handle its submission. A save on a separate hook never runs.
  • Forgetting the nonce in the settings form makes it writable by any request that can reach the dashboard.
  • A slow query here is felt on every dashboard load. no_found_rows and a small posts_per_page are the cheap wins.
  • The widget ID is stored in each user’s hidden and ordered widget preferences. Changing it makes the widget reappear for people who had hidden it.
  • Forcing a widget to the top by reordering $wp_meta_boxes fights the user’s own drag and drop, and it breaks quietly when the array shape changes.
  • wp_count_posts() is cached per post type, but calling it for several types on one screen adds up.
  • Output in a dashboard widget still has to be escaped. Post titles come from users.

Compatibility

wp_add_dashboard_widget() and wp_dashboard_setup have been stable since WordPress 2.7, and the settings callback argument since 3.0. The $wp_meta_boxes reordering used for the top position relies on an internal global, which is why it is not the default here. The generated code targets PHP 7.0 and up, and the tool runs entirely in your browser.

Frequently asked questions

Why does my widget not appear?
Either the capability check is failing for your user, or the registration is on the wrong hook. It has to be wp_dashboard_setup.
Where does the Configure link come from?
Passing a fourth argument to wp_add_dashboard_widget(). With null there is no link.
Why is my settings form not saving?
The save has to happen inside the settings callback. WordPress calls it again on submit with $_POST populated.
Can I put the widget in the side column?
Yes, but the position argument is advisory: users can drag widgets wherever they like, and their choice is remembered.
How do I remove a core dashboard widget?
remove_meta_box( 'dashboard_quick_press', 'dashboard', 'side' ) on wp_dashboard_setup, with a later priority.

From the people who built this tool

WP Adminify

The WordPress admin, rebuilt: a dashboard worth looking at, menu and column control, a real file manager and the login page your client sees.

See WP Adminify Free version on WordPress.org

Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.