Better way to handle WordPress Dashboard widgets with options
This tutorial was inspired by an example that I found on official WordPress Codex documentation here. Once I tried that example on my own website, I decided that it is far away from perfect.
Let me guide though two steps of creating a dashboard widget with options and modifying it.
Step 1. Begin with creating a default WordPress Dashboard Widget with options
As I already said above, it is the simplified example from WP Codex. You can insert it to your current theme functions.php
file (but better – in child theme or custom plugin).
- In my previous tutorial I mentioned that you can use
add_meta_box()
function instead ofwp_add_dashboard_widget()
, but no way to do it if you’re creating a dashboard widget with options, becausewp_add_dashboard_widget()
has the 4th argument which allows to display a form with options. - Below I save the widget settings in WordPress options (
get_option()
andupdate_option()
functions). But if your widget has a lot of settings, the good practice is to store them in the same option as an array. - My widget allows to display a custom post content in it.
- Below
custom_post
is the option name,my_custom_post
is the form field name.
add_action( 'wp_dashboard_setup', 'prefix_add_dashboard_widget' );
function prefix_add_dashboard_widget() {
wp_add_dashboard_widget(
'misha_dashboard_widget', // widget ID
'Custom Dashboard Widget', // widget title
'misha_dashboard_widget', // callback #1 to display it
'misha_process_my_dashboard_widget' // callback #2 for settings
);
}
/*
* Callback #1 function
* Displays widget content
*/
function misha_dashboard_widget() {
// if the widget is configured and the post is exists
if( $post = get_post( get_option( 'custom_post' ) ) ) {
$c = do_shortcode( html_entity_decode( $post->post_content ) );
echo "<h2>{$post->post_title}</h2><p>{$c}</p>";
} else {
echo 'Widget is not configured.';
}
}
/*
* Callback #2 function
* This function displays your widget settings
*/
function misha_process_my_dashboard_widget() {
// basic checks and save the widget settings here
if( 'POST' == $_SERVER['REQUEST_METHOD']
&& isset( $_POST['my_custom_post'] ) ) {
update_option( 'custom_post', absint($_POST['my_custom_post']) );
}
echo '<h3>Select a page that will be displayed in this widget</h3>'
. wp_dropdown_pages( array(
'post_type' => 'page',
'selected' => get_option( 'custom_post' ),
'name' => 'my_custom_post',
'id' => 'my_custom_post',
'show_option_none' => '- Select -',
'echo' => false
) );
}
Step 1 can work standalone, so you do not need to implement the second step which is all about AJAX. Here is how it works.

Step 2. Make it AJAX
If you would like your widget to work more smoothly, you must consider implementing this step too (you can scroll down first to look at the demo).
2.1 Some CSS for slick opacity animation
If you add this code, the “Configure” button will appear more smoothly. No reason to create a separate CSS file for it, so I recommend to add it via admin_head
action hook.
.edit-box.open-box{
transition: opacity .2s;
}
2.2 jQuery code to process asynchronous requests
As for JavaScript code below, it is better to connect it to your admin area with a separate .js file and admin_enqueue_scripts
action hook. But admin_head
is also ok.
Please note, that #misha_dashboard_widget
is your widget ID that we set in Step 1, line 5.
jQuery(function($){
// the Configure link click event
$('#misha_dashboard_widget .edit-box.open-box').click(function(){
var button = $(this);
$.ajax({
url: ajaxurl, // it is predefined in /wp-admin/
type: 'POST',
data: 'action=showform',
beforeSend : function( xhr ){
// add preloader
button.hide().before('<span class="spinner" style="visibility:visible;display:block;margin:0 0 0 15px"></span>');
},
success : function( data ){
// remove preloader
button.prev().remove();
// insert settings form
$('#misha_dashboard_widget').find('.inside').html(data);
}
});
return false;
});
// form submit event
$('body').on('submit', '#misha_widget_settings', function(){
var form = $(this);
$.ajax({
url: ajaxurl,
type: 'POST',
data: $(this).serialize(), // all form fields
beforeSend : function( xhr ){
// add preloader just after the submit button
form.find('.submit').append('<span class="spinner" style="display:inline-block;float:none;visibility:visible;margin:0 0 0 15px"></span>');
},
success : function( data ){
$('#misha_dashboard_widget').find('.inside').html(data);
// show the Configure link again
$('#misha_dashboard_widget .edit-box.open-box').show();
}
});
return false;
});
});
Do not forget to check that no errors have appeared in your browser console.
2.3 Process AJAX requests in PHP
Insert the below code in the same place where you inserted the code from Step 1. Some notes:
- In the first step there were no security checks, because WordPress do it itself. But below I’ve added a nonce field (line 21) and validate it with
check_ajax_referer()
(line 34). - We do not need
wp_ajax_nopriv_
action hook because the code is for admin area usage only.
/*
* This action hook shows settings form
*/
add_action( 'wp_ajax_showform', 'misha_ajax_show_form' ); // wp_ajax_{ACTION}
function misha_ajax_show_form(){
// widget ID should match but it is not required
$widget_id = 'misha_dashboard_widget';
echo '<form method="post" id="misha_widget_settings">
<h3>Select a page that will be displayed in widget</h3>'
. wp_dropdown_pages( array(
'post_type' => 'page',
'selected' => get_option( 'custom_post' ),
'name' => 'my_custom_post',
'id' => 'my_custom_post',
'show_option_none' => '- Select -',
'echo' => false
) )
. '<input type="hidden" name="action" value="widgetsave" /><input type="hidden" name="widget_id" value="' . $widget_id . '">'
. wp_nonce_field( 'edit-dashboard-widget_' . $widget_id, 'dashboard-widget-nonce', true, false )
. '<p class="submit"><input type="submit" name="submit" id="submit" style="display:inline-block" class="button button-primary" value="Submit"></p></form>';
die;
}
/*
* This action hook saves the settings and displays the widget content
*/
add_action( 'wp_ajax_widgetsave', 'misha_save_widget' ); // wp_ajax_{ACTION}
function misha_save_widget(){
// security check
check_ajax_referer( 'edit-dashboard-widget_' . $_POST['widget_id'], 'dashboard-widget-nonce' );
$post_id = absint( $_POST['my_custom_post'] );
update_option( 'custom_post', $post_id );
if( $post = get_post( $post_id ) ) {
$c = do_shortcode( html_entity_decode( $post->post_content ) );
echo "<h2>{$post->post_title}</h2><p>{$c}</p>";
} else {
echo 'Widget is not configured.';
}
die;
}
Here is the result:
[videoloop mp4=”https://rudrastyh.com/wp-content/uploads/2018/04/dashboard-widgets.mp4″ w=”792″ h=”442″]

Misha Rudrastyh
Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.
Need some developer help? Contact me