The WordPress admin dashboard is often the first thing a client sees when they log in. By default it’s filled with widgets that are either useless to non-technical users, the WordPress news feed, the quick draft, site health, or presented without context. Adding a custom branded widget that greets the user by name and provides direct links to the tasks they actually perform creates a much more professional and client-friendly experience.
The Code
Add this to your functions.php or a site-specific plugin. It registers a new dashboard widget using wp_add_dashboard_widget() and then repositions it to the top of the dashboard using a global meta box manipulation, so it’s the first thing the client sees, not buried below the default widgets.
How wp_add_dashboard_widget Works
wp_add_dashboard_widget() takes three required arguments: a unique widget ID, the widget title shown in the header bar, and the name of the callback function that renders the widget content. Optional fourth and fifth arguments accept a control callback and control arguments for widgets that need settings forms, which most simple widgets don’t need.
Moving the Widget to the Top
Dashboard widgets are stored in the global $wp_meta_boxes array. After registering the widget, the snippet extracts it from its default position in the normal/core group, unsets it from the array, and then re-inserts it at the beginning of the array using array_merge(). This ensures it renders at the top of the left column when the dashboard loads.
The Widget Content
The example widget renders a personalised greeting using wp_get_current_user(), a short list of quick-action links, and a simple stat line showing the number of published posts and pages from wp_count_posts(). All output is escaped, esc_html() for the display name and esc_url() for links.
Removing Default Widgets
A custom welcome widget works best when paired with removal of the cluttered default widgets. You can remove them on the same wp_dashboard_setup hook using remove_meta_box(). Common ones to remove for client sites include dashboard_primary (WordPress news), dashboard_quick_press (quick draft), and dashboard_activity. Removing dashboard_site_health is also common for clients who don’t need to manage technical site health checks.
Role-Specific Content
The render callback can include current_user_can() checks to show different content to different roles. An administrator might see database stats and plugin update counts, while an editor sees only content-related links. This makes a single widget serve multiple user types without requiring separate registrations.
add_action( 'wp_dashboard_setup', function() {
wp_add_dashboard_widget(
'nahnu_welcome_widget',
'Welcome to Your Website',
'nsl_render_welcome_widget'
);
// Move our widget to the top of the dashboard
global $wp_meta_boxes;
$widget = $wp_meta_boxes['dashboard']['normal']['core']['nahnu_welcome_widget'];
unset( $wp_meta_boxes['dashboard']['normal']['core']['nahnu_welcome_widget'] );
$wp_meta_boxes['dashboard']['normal']['core'] = array_merge(
[ 'nahnu_welcome_widget' => $widget ],
$wp_meta_boxes['dashboard']['normal']['core']
);
} );
function nsl_render_welcome_widget() {
$user = wp_get_current_user();
$post_count = wp_count_posts()->publish;
$page_count = wp_count_posts( 'page' )->publish;
?>
<p>Hello, <strong><?php echo esc_html( $user->display_name ); ?></strong>! Here are a few quick links to get started.</p>
<ul style="margin:12px 0;padding-left:18px">
<li><a href="<?php echo esc_url( admin_url( 'post-new.php' ) ); ?>">Write a new post</a></li>
<li><a href="<?php echo esc_url( admin_url( 'media-new.php' ) ); ?>">Upload media</a></li>
<li><a href="<?php echo esc_url( admin_url( 'customize.php' ) ); ?>">Customise the site</a></li>
</ul>
<p style="margin-top:12px;padding-top:12px;border-top:1px solid #f0f0f0;font-size:13px;color:#64748b">
<?php echo $post_count; ?> published posts · <?php echo $page_count; ?> pages
</p>
<?php
}
