So, you want to add a custom admin notice in WordPress that not only pops up but also lets users get rid of it and remembers their choice? That’s a smart move. It helps keep your WordPress dashboard clean and focuses on what users actually need to see. Think of it as adding a helpful sticky note that only the person who needs it sees, and they can just flick it away when they’re done.
Adding a dismissible, user-specific admin notice in WordPress involves a bit of code, but it’s totally doable. We’ll be diving into how to store the dismissal status (so it remembers who dismissed what), how to display the notice, and critically, how to make that ‘dismiss’ button actually work, keeping it gone for that specific user. We’ll break it down step-by-step, explaining what’s happening along the way.
First off, why would you even bother with this? Custom admin notices are fantastic for communicating important information without cluttering up the main content area.
Common Use Cases for Admin Notices
- Plugin Updates or Alerts: Letting users know about recommended updates or crucial security patches.
- Onboarding Tips: Guiding new users through key features or settings after a fresh install.
- Configuration Reminders: Prompting users to complete a specific setup step.
- System Status Messages: Informing admins about potential issues or maintenance periods.
- Feature Introductions: Announcing new functionalities and how to access them.
The “Dismissible” and “Per-User” Magic
The “dismissible” part is crucial for user experience. Nobody likes persistent pop-ups. Allowing users to dismiss a notice means they can clear their dashboard when they’re done with the information.
The “per-user” aspect takes it a step further. Imagine you have multiple administrators on a site. If one admin dismisses a notice, it shouldn’t disappear for everyone else. Each user should have their own control over what they see. This is where we’ll be focusing our technical efforts.
If you’re looking to enhance your WordPress admin experience by adding custom admin notices that are both dismissible and persistent for each user, you might find it helpful to check out a related article that dives deeper into this topic. This resource provides step-by-step instructions and code snippets to help you implement this feature effectively. For more information, visit this article.
The Code Foundation: Hooks and Actions
WordPress is built on action and filter hooks. These are the essential tools we’ll use to inject our custom code into the WordPress environment.
Understanding WordPress Hooks
Essentially, hooks are points in the WordPress core, themes, or plugins where developers can “hook into” to add or modify functionality without altering the core files. This is super important for maintainability and preventing your changes from being lost during updates.
- Action Hooks: These hooks allow you to execute a function at a specific point in the WordPress execution flow. For example,
admin_noticesis an action hook that fires in the backend, making it the perfect place to display our custom notices. - Filter Hooks: While not our primary focus here, filters allow you to modify data before it’s used or displayed.
The admin_notices Hook
This is our main stage for displaying our notice. Whenever WordPress renders notices in the admin area, this hook is triggered. We’ll attach a function to this hook that checks if our notice should be displayed for the current user.
Where to Put Your Code
- Your Theme’s
functions.phpFile: This is the simplest place for small, site-specific customizations. However, if you switch themes, your notices will disappear. - A Custom Plugin: This is the recommended approach for anything beyond very minor tweaks. It keeps your functionality separate from your theme, ensuring it persists even if you change your theme. For this guide, we’ll assume you’re comfortable creating a simple plugin or adding to your
functions.phpif you’re just experimenting.
Displaying Your Custom Notice
Now for the fun part: actually making something appear!
The Core Function for Display
We’ll create a PHP function that will be hooked into admin_notices. This function will contain the HTML for our notice and the logic for checking if it should be shown.
“`php
// In your plugin file or theme’s functions.php
function my_custom_admin_notice() {
// Check if the current user has dismissed this notice
if ( ! current_user_can( ‘manage_options’ ) ) {
// Optional: Only show to specific roles, e.g., administrators
return;
}
// Get the current user’s ID
$user_id = get_current_user_id();
$notice_id = ‘my_important_notice’; // A unique identifier for this notice
// Check if the user has dismissed this notice
$dismissed = get_user_meta( $user_id, ‘dismissed_admin_notices’, true );
if ( ! empty( $dismissed ) && is_array( $dismissed ) && in_array( $notice_id, $dismissed ) ) {
// User has dismissed this notice, so do nothing
return;
}
// If not dismissed, display the notice
?>
This is my important custom admin notice!
It contains crucial information you need to know about your site.
If you’re looking to enhance your WordPress admin experience, you might find it useful to explore a related article on creating custom admin notices that are not only dismissible but also persist for individual users. This can greatly improve user interaction and streamline your admin dashboard. For more insights on this topic, check out this informative piece on admin notices that can help you implement these features effectively.
Implementing Per-User Dismissal: The JavaScript Solution
To make the dismissal permanent for a specific user, we need to intercept the click on the dismiss button, record that the user has dismissed this notice, and then then visually hide it. This requires a bit of JavaScript.
Enqueuing Your Custom JavaScript
You need to load your JavaScript file only in the WordPress admin area. You’ll do this using the admin_enqueue_scripts action hook.
Add this to your functions.php file or your plugin file:
“`php
// In your plugin file or theme’s functions.php
function my_custom_admin_notice_script() {
// Only load this script on admin pages and for users who can manage options
if ( ! is_admin() || ! current_user_can( ‘manage_options’ ) ) {
return;
}
// Register and enqueue your JavaScript file
wp_enqueue_script(
‘my-custom-admin-notice-script’, // Unique handle for your script
get_template_directory_uri() . ‘/js/admin-notices.js’, // Path to your JS file (adjust if in plugin)
array( ‘jquery’ ), // Dependencies (jQuery is common)
‘1.0’, // Version number
true // Load in footer
);
// Pass data to your JavaScript if needed (optional but good practice)
// For example, you might want to pass the nonce for security if doing AJAX
// wp_localize_script( ‘my-custom-admin-notice-script’, ‘myAdminNoticeParams’, array(
// ‘ajax_url’ => admin_url( ‘admin-ajax.php’ ),
// ‘nonce’ => wp_create_nonce( ‘dismiss_notice_nonce’ ),
// ) );
}
add_action( ‘admin_enqueue_scripts’, ‘my_custom_admin_notice_script’ );
?>
“`
Explanation for the JavaScript Enqueue:
is_admin()andcurrent_user_can( 'manage_options' ): Ensures the script only loads in the admin and for relevant users.wp_enqueue_script(): This WordPress function is used to properly load JavaScript files.'my-custom-admin-notice-script': A unique name (handle) for your script.get_template_directory_uri() . '/js/admin-notices.js': This is the path to your JavaScript file. If you’re using a theme, it assumes ajsfolder in your theme’s root directory with a file namedadmin-notices.js. If you are creating a plugin, you’ll need to adjust this path. For a plugin, it would typically look something like:
“`php
plugin_dir_url( __FILE__ ) . ‘js/admin-notices.js’,
“`
(assuming your JS is in a js subfolder of your plugin).
array( 'jquery' ): This specifies that your script depends on jQuery.'1.0': A version number. Useful for cache busting.true: Tells WordPress to load the script in the footer of the page, which is generally good for performance.
The JavaScript File (admin-notices.js)
Now, create the admin-notices.js file in the location you specified above and add the following code:
“`javascript
jQuery(document).ready(function($) {
// Target our specific notice by its ID
// Make sure this ID matches the ID in your PHP notice HTML
var noticeId = ‘#my-custom-notice’;
// Use WordPress’s built-in dismissible logic, but hijack the click
$(document).on(‘click’, noticeId + ‘ .notice-dismiss’, function(e) {
e.preventDefault(); // Prevent the default dismissal action
// Get the current user ID (if available or passed from PHP)
// For simplicity here, we’ll rely on WordPress’s user settings to save
// the dismissal, but in more complex scenarios, you might use AJAX.
// Get the unique notice identifier from the notice div’s data attribute
// Let’s assume we’ve added a data-notice-id attribute to our notice HTML like:
//
// This is crucial for identifying which notice was dismissed.
var $notice = $(this).closest(noticeId);
var noticeIdentifier = $notice.data(‘notice-id’);
if (!noticeIdentifier) {
console.error(‘Admin notice identifier not found. Add data-notice-id attribute to the notice.’);
return;
}
// Now, we need to save this dismissal status for the current user.
// The simplest way is to trigger a WordPress AJAX action.
// We’ll need a PHP AJAX handler for this.
// For this simplified example, we’ll just hide it visually.
// For persistent dismissal, we’ll explain the AJAX approach in the next section.
$notice.hide();
// This is where the real persistence logic goes
// For now, we’ve just hidden it. The next section will add AJAX.
// This part is just to see the JS running and visually hiding.
// To make it truly dismissible per user, we need to save this.
// Let’s look at the AJAX approach.
});
// Important Note: The above simply hides the notice visually.
// For true persistence, we need to save this state.
// The next section will detail how to do this using AJAX.
});
“`
Crucial Update to the PHP my_custom_admin_notice function:
We need to add a data- attribute to our notice HTML so our JavaScript can easily identify which* notice is being dismissed.
“`php
// In your plugin file or theme’s functions.php
function my_custom_admin_notice() {
if ( ! current_user_can( ‘manage_options’ ) ) {
return;
}
$user_id = get_current_user_id();
$notice_id = ‘my_important_notice’; // Unique identifier for this notice
$saved_dismissed_notices = get_user_meta( $user_id, ‘dismissed_admin_notices’, true );
if ( ! empty( $saved_dismissed_notices ) && is_array( $saved_dismissed_notices ) && in_array( $notice_id, $saved_dismissed_notices ) ) {
return;
}
// Add the data-notice-id attribute
?>
}
add_action( ‘admin_notices’, ‘my_custom_admin_notice’ );
// Also adjust the JS enqueue path if you’re in a plugin
function my_custom_admin_notice_script() {
if ( ! is_admin() || ! current_user_can( ‘manage_options’ ) ) {
return;
}
// Adjust path for plugin vs theme
$script_url = ”;
if ( defined(‘PLUGIN_DIR_URL’) ) { // Assuming a constant or plugin context
$script_url = PLUGIN_DIR_URL . ‘js/admin-notices.js’; // Example for a plugin
} else {
$script_url = get_template_directory_uri() . ‘/js/admin-notices.js’; // Example for theme
}
wp_enqueue_script(
‘my-custom-admin-notice-script’,
$script_url, // Use the determined path
array( ‘jquery’ ),
‘1.0’,
true
);
}
add_action( ‘admin_enqueue_scripts’, ‘my_custom_admin_notice_script’ );
?>
“`
If you’re looking to enhance your WordPress admin experience, you might find it useful to explore a related article on creating custom admin notices. This resource provides step-by-step guidance on how to implement dismissible notices that can be tailored for individual users. You can read more about it in this insightful piece on custom admin notices, which complements the topic of adding persistent notifications effectively.
Saving the Dismissal State: The AJAX Approach
The JavaScript above only hides the notice visually. To make it persist across page loads and sessions, we need server-side action. This is where WordPress AJAX comes in.
Understanding WordPress AJAX
WordPress has a built-in AJAX handler (admin-ajax.php) that allows your JavaScript to communicate with your PHP code without a full page reload. You can trigger actions and receive responses.
Setting up the PHP AJAX Handler
We need a function hooked into WordPress AJAX actions to receive the dismissal request and save the user meta.
Add this to your functions.php file or plugin file:
“`php
// In your plugin file or theme’s functions.php
// Hook for logged-in users to perform AJAX actions
add_action( ‘wp_ajax_dismiss_admin_notice’, ‘handle_dismiss_admin_notice’ );
function handle_dismiss_admin_notice() {
// Verify the nonce for security
if ( ! isset( $_POST[‘nonce’] ) || !wp_verify_nonce( $_POST[‘nonce’], ‘dismiss_notice_nonce’ ) ) {
wp_send_json_error( ‘Security check failed!’ );
wp_die();
}
// Get the notice ID from the POST data
if ( ! isset( $_POST[‘notice_id’] ) || empty( $_POST[‘notice_id’] ) ) {
wp_send_json_error( ‘Notice ID not provided!’ );
wp_die();
}
$notice_id = sanitize_text_field( $_POST[‘notice_id’] ); // Sanitize notice ID
$user_id = get_current_user_id();
// Make sure we have a valid user ID
if ( ! $user_id ) {
wp_send_json_error( ‘User not logged in or anonymous!’ );
wp_die();
}
// Get current list of dismissed notices
$dismissed_notices = get_user_meta( $user_id, ‘dismissed_admin_notices’, true );
// Ensure it’s an array
if ( ! is_array( $dismissed_notices ) ) {
$dismissed_notices = array();
}
// Add the new notice ID if it’s not already there
if ( ! in_array( $notice_id, $dismissed_notices ) ) {
$dismissed_notices[] = $notice_id;
}
// Save the updated array back to user meta
update_user_meta( $user_id, ‘dismissed_admin_notices’, $dismissed_notices );
// Send a success response back to JavaScript
wp_send_json_success( ‘Notice dismissed successfully!’ );
wp_die(); // Always die after an AJAX handler
}
?>
“`
Explanation of the AJAX Handler:
add_action( 'wp_ajax_dismiss_admin_notice', 'handle_dismiss_admin_notice' );: This hooks ourhandle_dismiss_admin_noticefunction to a specific AJAX action nameddismiss_admin_notice. Thewp_ajax_prefix is for logged-in users.wp_verify_nonce( $_POST['nonce'], 'dismiss_notice_nonce' ): Crucial for security. Nonces are one-time tokens used to verify that the request came from your WordPress site and not from a malicious source.$_POST['notice_id']: We expect the JavaScript to send the notice’s unique identifier via POST data.get_current_user_id(): Retrieves the current user’s ID.get_user_meta()andupdate_user_meta(): These are used to retrieve and save thedismissed_admin_noticesarray for the current user.wp_send_json_error()/wp_send_json_success(): Functions to send JSON responses back to the JavaScript.wp_die();: WordPress AJAX handlers must end withwp_die()to prevent unintended output.
Modifying the JavaScript to Trigger AJAX
Now, let’s update our admin-notices.js file to actually send the AJAX request.
“`javascript
jQuery(document).ready(function($) {
// Target our specific notice by its ID
var noticeSelector = ‘#my-custom-notice’;
// Use WordPress’s built-in dismissible logic, but hijack the click
$(document).on(‘click’, noticeSelector + ‘ .notice-dismiss’, function(e) {
e.preventDefault(); // Prevent the default dismissal action
var $notice = $(this).closest(noticeSelector);
var noticeIdentifier = $notice.data(‘notice-id’);
if (!noticeIdentifier) {
console.error(‘Admin notice identifier not found. Add data-notice-id attribute to the notice.’);
return;
}
// Hide the notice visually now
$notice.hide();
// Trigger the AJAX call to save dismissal
// We need to pass the notice ID and a nonce for security.
// The nonce needs to be passed from PHP using wp_localize_script if used.
// For simplicity here, let’s assume we passed it.
// If you haven’t used wp_localize_script, you’ll need it.
// Let’s add it to the enqueue function first.
// Assuming you’ve added wp_localize_script in functions.php like:
// wp_localize_script( ‘my-custom-admin-notice-script’, ‘myAdminNoticeParams’, array(
// ‘ajax_url’ => admin_url( ‘admin-ajax.php’ ),
// ‘nonce’ => wp_create_nonce( ‘dismiss_notice_nonce’ ),
// ) );
// Check if myAdminNoticeParams is available (it should be if localized)
if (typeof myAdminNoticeParams === ‘undefined’) {
console.error(‘myAdminNoticeParams not defined. Did you localize the script?’);
return;
}
$.ajax({
url: myAdminNoticeParams.ajax_url, // Use the localized AJAX URL
type: ‘POST’,
data: {
action: ‘dismiss_admin_notice’, // This matches the wp_ajax_ hook prefix
notice_id: noticeIdentifier,
nonce: myAdminNoticeParams.nonce // Use the localized nonce
},
success: function(response) {
if (response.success) {
console.log(‘Dismissal saved: ‘ + response.data);
} else {
console.error(‘Error saving dismissal: ‘, response.data);
// Optionally, you might want to show the notice again if saving failed?
// Or at least log a more detailed error.
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.error(‘AJAX request failed: ‘, textStatus, errorThrown);
// Handle network errors or server issues
}
});
});
});
“`
And crucially, update your admin_enqueue_scripts function in PHP to include wp_localize_script so your JavaScript can access the AJAX URL and nonce:
“`php
// In your plugin file or theme’s functions.php
function my_custom_admin_notice_script() {
if ( ! is_admin() || ! current_user_can( ‘manage_options’ ) ) {
return;
}
$script_url = ”;
if ( defined(‘PLUGIN_DIR_URL’) ) { // Example for a plugin
$script_url = PLUGIN_DIR_URL . ‘js/admin-notices.js’;
} else { // Example for a theme
$script_url = get_template_directory_uri() . ‘/js/admin-notices.js’;
}
wp_enqueue_script(
‘my-custom-admin-notice-script’,
$script_url,
array( ‘jquery’ ),
‘1.0’,
true
);
// Localize the script to pass data to JavaScript
wp_localize_script( ‘my-custom-admin-notice-script’, ‘myAdminNoticeParams’, array(
‘ajax_url’ => admin_url( ‘admin-ajax.php’ ),
‘nonce’ => wp_create_nonce( ‘dismiss_notice_nonce’ ), // Make sure this matches JS
) );
}
add_action( ‘admin_enqueue_scripts’, ‘my_custom_admin_notice_script’ );
?>
“`
With these changes, when a user clicks the ‘x’ button on your notice:
- The JavaScript intercepts the click.
- It hides the notice visually.
- It sends an AJAX request to your WordPress site.
- Your PHP AJAX handler receives the request, verifies it, and saves the
$notice_idto the current user’s meta. - The next time the user visits an admin page, the
my_custom_admin_noticefunction will check their meta, find the$notice_id, and not display the notice.
Best Practices and Considerations
You’ve got the core functionality down. Now, let’s talk about making it robust and user-friendly.
Multiple Notices and User Meta
If you plan to have more than one dismissible notice, your dismissed_admin_notices user meta should store an array of all dismissed notice IDs. Your PHP code already handles this correctly by checking in_array() and adding to the array.
Storing Notice IDs
Always use unique, descriptive IDs for your notices. Avoid spaces or special characters. For example:
welcome_message_v1critical_update_reminder_2023new_feature_intro_plugin_x
Advanced Targeting
You might want to show notices only to specific user roles or users who haven’t completed a certain task.
- User Roles: Modify the
current_user_can()check in yourmy_custom_admin_noticefunction. For example, to show only to administrators and editors:
“`php
if ( ! current_user_can( ‘manage_options’ ) && ! current_user_can( ‘edit_pages’ ) ) {
return;
}
“`
- User Capabilities: You can check for specific user capabilities if needed.
- Custom User Meta: If a notice depends on a user having performed a specific action (e.g., configuring a setting), you can store a custom user meta flag and check for that.
Error Handling and User Feedback
- JavaScript Error Logging: The
console.errormessages are helpful for developers. For production, you might want more sophisticated error logging. - AJAX Failures: If the AJAX call fails, the notice will reappear on the next refresh. You could add a message to the user indicating that the dismissal couldn’t be saved and they might need to try again or contact support.
- User Experience: Ensure your notices are concise and actions are clear. Providing a direct link to the relevant page for more information is a good practice.
Updating/Removing Notices
If you need to re-enable a notice that was previously dismissed, you would need to create another mechanism (e.g., a settings page where an admin can “reset” dismissals for themselves or all users). For example, you could add a button on a settings page that clears the dismissed_admin_notices user meta for the current user.
Security Considerations
- Nonces: Always use nonces for AJAX requests to prevent cross-site request forgery (CSRF) attacks.
- Sanitization: Sanitize all data received from the client side (like
$_POST['notice_id']) before using or saving it.sanitize_text_field()is a good start. - Capabilities: Restrict who can see and dismiss notices based on user roles and capabilities.
Conclusion
You’ve now got a solid blueprint for creating custom admin notices in WordPress that are both dismissible and persist per user. By combining PHP hooks, intelligent user meta storage, and a touch of JavaScript for the user interaction, you can significantly improve the usability and communication within your WordPress admin area. This approach keeps your dashboard clutter-free and ensures that important messages reach the right people without being annoying. Remember to always prioritize security and good coding practices, especially when dealing with user data and AJAX requests.