How to Add Custom Widgets to the WordPress Admin Dashboard

The WordPress admin dashboard is composed of widget-like panels called “meta boxes”. You can add your own — to display plugin stats, a quick-links panel, a support contact section — or remove the default ones to clean up the interface for clients.

Problem: How do you add a custom information widget to the WordPress admin dashboard — for example, showing recent entries, post counts, or data from an external API?

Solution: Use wp_add_dashboard_widget() inside a wp_dashboard_setup action hook, providing a handle, a title, and a callback that outputs the widget's HTML content.

Add a custom dashboard widget:

add_action( 'wp_dashboard_setup', 'register_my_dashboard_widgets' );

function register_my_dashboard_widgets() {
    wp_add_dashboard_widget(
        'my_plugin_stats',              // Widget ID (slug)
        __( 'My Plugin Stats', 'td' ),  // Title
        'render_my_dashboard_widget',   // Callback
        'configure_my_dashboard_widget' // Optional control/settings callback
    );
}

function render_my_dashboard_widget() {
    $count = wp_count_posts( 'portfolio' )->publish;
    echo '<p>' . sprintf(
        esc_html__( 'You have %d published portfolio items.', 'td' ),
        $count
    ) . '</p>';
    echo '<p><a href="' . esc_url( admin_url( 'edit.php?post_type=portfolio' ) ) . '">'
         . esc_html__( 'Manage Portfolio', 'td' ) . '</a></p>';
}

function configure_my_dashboard_widget() {
    // Settings form (shown when the user clicks "Configure")
    if ( isset( $_POST['my_widget_option'] ) ) {
        update_option( 'my_widget_option', sanitize_text_field( $_POST['my_widget_option'] ) );
    }
    $value = get_option( 'my_widget_option', 'default' );
    echo '<label>' . esc_html__( 'Display mode:', 'td' ) . '
        <input type="text" name="my_widget_option" value="' . esc_attr( $value ) . '">
    </label>';
}

Remove default dashboard widgets (useful for client sites):

add_action( 'wp_dashboard_setup', 'remove_default_dashboard_widgets' );

function remove_default_dashboard_widgets() {
    remove_meta_box( 'dashboard_right_now',        'dashboard', 'normal' ); // At a Glance
    remove_meta_box( 'dashboard_activity',          'dashboard', 'normal' ); // Activity
    remove_meta_box( 'dashboard_quick_press',       'dashboard', 'side'   ); // Quick Draft
    remove_meta_box( 'dashboard_primary',           'dashboard', 'side'   ); // WordPress News
    remove_meta_box( 'woocommerce_dashboard_status','dashboard', 'normal' ); // WooCommerce Status
}

NOTE: Dashboard widget positions are stored in user meta, so each user can rearrange them independently. If you want to force a specific layout for all users, hook into get_user_option_screen_layout_dashboard and set column counts accordingly.