Receiving star ratings or testimonials for your WordPress plugin can be a fruitless endeavor. The best way to get ratings is to ask for them, whether in the support forums, over email, or even in the plugin settings itself.
In this tutorial, I’ll show you how to add a ratings admin notice so you can ask for a rating after a specified amount of time. First, let’s go over some guidelines so that you don’t run afoul of the WordPress Plugin Guidelines.
A brief summary of admin notice guidelines
Since we’ll be creating an admin notice as part of the ratings nag, let’s try not to irritate our users too much.
According to the guidelines, our admin notice should not hijack the admin screen.
Users prefer and expect plugins to feel like part of WordPress. Constant nags and overwhelming the admin dashboard with unnecessary alerts detract from this experience.
The admin notice we create needs to be as unobtrusive as possible, must be dismissable, and should ideally only show up on our own admin screen and not anywhere else.
With that being said, let’s go ahead and create our ratings admin notice.
View the Code on GitHub
The code demonstrated in this tutorial is available on GitHub as a downloadable plugin.
The plugin headers
I’m calling this plugin “DLX Ratings Nag”, so let’s fill out the plugin headers for the plugin.
<?php
/**
* Plugin Name: DLX Ratings Nag
* Plugin URI: https://dlxpplugins.com/plugins/
* Description: Demonstrates how to add a ratings admin notice to your plugin.
* Author: DLX Plugins
* Version: 0.0.1
* Requires at least: 5.1
* Requires PHP: 7.2
* Author URI: https://dlxplugins.com
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
*
* @package DLXRatingsNag
*/
namespace DLXPlugins\DLXRatingsNag;
Code language: PHP (php)
Set an option on activation
To show the ratings nag, we’ll set an option on activation that will set a countdown on when to show the ratings admin notice.
It’s not a great idea to show a ratings notice when a user is brand new to a plugin. It’s better to wait after a period of time before asking for a rating so that the user has had a chance to become acclimated to your WordPress plugin.
Let’s set an option that will save the timestamp of the plugin installation.
// Register an activation hook to set an option to track how long the plugin has been installed.
register_activation_hook( __FILE__, __NAMESPACE__ . '\set_install_date' );
/**
* Set the install date option.
*
* @return void
*/
function set_install_date() {
update_option( 'dlx_ratings_nag_install_date', time() );
}
Code language: PHP (php)
In this case, I’m adding an option with key dlx_ratings_nag_install_date
with the timestamp of when the plugin was activated.
Creating the admin panel
Let’s do some very simple admin panel boilerplate so we have a place to show off our notice.
// Register an admin panel callback.
add_action( 'admin_menu', __NAMESPACE__ . '\add_admin_menu' );
/**
* Add an admin menu item.
*
* @return void
*/
function add_admin_menu() {
add_options_page(
__( 'DLX Ratings Nag', 'dlx-ratings-nag' ),
__( 'DLX Ratings Nag', 'dlx-ratings-nag' ),
'manage_options',
'dlx-ratings-nag',
__NAMESPACE__ . '\ratings_nag_page'
);
}
/**
* Display the ratings nag page.
*
* @return void
*/
function ratings_nag_page() {
?>
<div class="wrap">
<h1><?php esc_html_e( 'DLX Ratings Nag', 'dlx-ratings-nag' ); ?></h1>
</div>
<?php
}
Code language: PHP (php)
Here’s how everything should look at this point. We have a barebones admin panel, which we can add our ratings notice to.

Adding the admin notice
In order to add the admin notice, we need to get the hook name of our current screen. I’m going to do a quick var_dump
in order to get the screen argument.
/**
* Display the ratings nag page.
*
* @return void
*/
function ratings_nag_page() {
global $current_screen;
var_dump( $current_screen->id );
?>
<div class="wrap">
<h1><?php esc_html_e( 'DLX Ratings Nag', 'dlx-ratings-nag' ); ?></h1>
</div>
<?php
}
Code language: PHP (php)
It turns out the screen ID is: settings_page_dlx-ratings-nag
Let’s create an admin notice, but only load it on our specific admin screen. We’ll be using the admin_notices
WordPress action.
// Register an admin notice callback.
add_action( 'admin_notices', __NAMESPACE__ . '\ratings_nag' );
/**
* Display the ratings nag.
*
* @return void
*/
function ratings_nag() {
$screen = get_current_screen();
if ( 'settings_page_dlx-ratings-nag' !== $screen->id ) {
return;
}
// Get the install date. Exit if it doesn't exist.
$install_date = get_option( 'dlx_ratings_nag_install_date', false );
if ( ! $install_date ) {
return;
}
// Get the number of days since the plugin was installed.
$days_installed = round( ( time() - $install_date ) / DAY_IN_SECONDS );
// If less than 14 days, exit.
if ( $days_installed < 14 ) {
return;
}
// Display the ratings nag.
?>
<div class="notice notice-info is-dismissible">
<p>
<?php
printf(
/* translators: %s: Plugin name */
esc_html__( 'You have been using %s for %d days. Would you like to leave a review?', 'dlx-ratings-nag' ),
'<strong>' . esc_html__( 'DLX Ratings Nag', 'dlx-ratings-nag' ) . '</strong>',
$days_installed
);
?>
</p>
<p>
<a href="https://wordpress.org/support/plugin/dlx-ratings-nag/reviews/#new-post" class="button button-primary" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'Yes, I\'d love to!', 'dlx-ratings-nag' ); ?>
</a>
<a href="<?php echo esc_url( admin_url( 'options-general.php?page=dlx-ratings-nag' ) ); ?>" class="button button-secondary">
<?php esc_html_e( 'No, thanks.', 'dlx-ratings-nag' ); ?>
</a>
</p>
</div>
<?php
}
Code language: PHP (php)
In the code, I’m testing to make sure that the plugin has been installed for at least 14 days before showing the ratings notice. This will ensure the user has had a chance to properly use the plugin in order to make an informed rating.
Note the classes used for the notice: notice notice-info is-dismissable
.
In addition to notice-info
, you could also use notice-error
, notice-warning
, and notice-success
.
In order to test the notice, I’ve gone ahead and modified the 14 days to zero.
// If less than 14 days, exit.
if ( $days_installed < 0 /* 14 */ ) {
return;
}
Code language: PHP (php)
Here’s how the ratings notice looks.

Making the notice dismissable
There’s two approaches in order to make the notice dismissable. We could use user meta, which would make the notice dismissable per user. We could also make the dismiss options based, meaning if one user dismisses the notice, it is dismissed for all users. The benefit of using user meta is there is a potential for more eyeballs, but this could get annoying if the user is a developer utilizing multiple accounts.
Taking in the benefits and drawbacks, let’s make the notice dismissable and store the dismissal in user meta. Let’s also take into account the developer experience and hide the notice if debugging is enabled.
We’ll also need to code and enqueue some JavaScript and attach itself to the various buttons in the ratings nag. When a button is clicked, an Ajax request is sent, which sets a flag in user meta to hide the ratings nag. Let’s begin by hiding the ratings nag if debugging is enabled.
Hide the ratings nag if in debug mode
Let’s add a conditional in the ratings_nag
output that’ll return early if WP_DEBUG
is enabled.
function ratings_nag() {
// Skip redirect if WP_DEBUG is enabled.
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
return false;
}
/* ratings nag code below */
}
Code language: PHP (php)
Set up the ajax action
Next, let’s set up the ajax action. We’ll use action: dlx_ratings_nag_dismiss
.
// Set up the ajax action for dismissing the ratings nag.
add_action( 'wp_ajax_dlx_ratings_nag_dismiss', __NAMESPACE__ . '\dismiss_ratings_nag' );
/**
* Dismiss the ratings nag.
*
* @return void
*/
function dismiss_ratings_nag() {
// Check the nonce.
check_ajax_referer( 'dlx-ratings-nag', 'nonce' );
// Get the current user ID.
$user_id = get_current_user_id();
// Set user meta to dismiss the ratings nag.
update_user_meta( $user_id, 'dlx_ratings_nag_dismissed', true );
// Return a success message.
wp_send_json_success();
}
Code language: PHP (php)
We’re checking for a nonce, which we’ll define later. If that passes, we get the user ID, and then set a user meta key that the notice has been dismissed.
Next, we’ll have to create the JavaScript, which will listen for a dismissal.
Capturing the dismissal events with JavaScript
First, we’ll need to know the HTML structure of the notice in order to target it via JavaScript. I’ve modified the HTML of the ratings notice in order to better target the elements using JavaScript.
/**
* Display the ratings nag.
*/
function ratings_nag() {
// Skip ratings nag if WP_DEBUG is enabled.
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
return false;
}
$screen = get_current_screen();
if ( 'settings_page_dlx-ratings-nag' !== $screen->id ) {
return;
}
// Get the install date. Exit if it doesn't exist.
$install_date = get_option( 'dlx_ratings_nag_install_date', false );
if ( ! $install_date ) {
return;
}
// Get the number of days since the plugin was installed.
$days_installed = round( ( time() - $install_date ) / DAY_IN_SECONDS );
// If less than 14 days, exit.
if ( $days_installed < 0 /* 14 */ ) {
return;
}
// Display the ratings nag.
?>
<div class="notice notice-info notice-rating is-dismissible">
<p>
<?php
printf(
/* translators: %s: Plugin name */
esc_html__( 'You have been using %1$s for %2$d days. Would you like to leave a review?', 'dlx-ratings-nag' ),
'<strong>' . esc_html__( 'DLX Ratings Nag', 'dlx-ratings-nag' ) . '</strong>',
$days_installed
);
?>
</p>
<p>
<a href="https://wordpress.org/support/plugin/dlx-ratings-nag/reviews/#new-post" class="button button-primary yes-button" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'Yes, I\'d love to!', 'dlx-ratings-nag' ); ?>
</a>
<a href="<?php echo esc_url( admin_url( 'options-general.php?page=dlx-ratings-nag' ) ); ?>" class="button button-secondary dismissable-button">
<?php esc_html_e( 'No, thanks.', 'dlx-ratings-nag' ); ?>
</a>
</p>
</div>
<?php
}
Code language: PHP (php)
We can target the notice with the is-dismissable
class along with notice
. For the buttons, we can target both anchors, so that clicking on each one also triggers a dismissal. And we can use class notice-dismiss
to target the close icon.

For the JavaScript, I’m creating a new folder called js
and placing a file called ratings-dismiss.js
in it.
.
└── dlxratingsnag/
├── js/
│ └── ratings-dismiss.js
└── dlxratingsnag.php
Code language: AsciiDoc (asciidoc)
Within ratings-dismiss.js
, I’ll place some onload code with a console output, and then work on enqueuing the file.
/**
* Onload, set up dismiss events. Vanilla JS.
*/
document.addEventListener('DOMContentLoaded', function() {
console.log( 'ratings-dismiss.js loaded' );
} );
Code language: JavaScript (javascript)
Next, we’ll enqueue the file.
add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\enqueue_scripts' );
/**
* Enqueue scripts.
*
* @return void
*/
function enqueue_scripts() {
$screen = get_current_screen();
if ( 'settings_page_dlx-ratings-nag' !== $screen->id ) {
return;
}
wp_enqueue_script(
'dlx-ratings-nag',
plugins_url( 'js/ratings-dismiss.js', __FILE__ ),
array(),
'0.0.1',
true
);
}
Code language: PHP (php)
Checking the console will reveal that the script is loading correctly.

In the JavaScript, I have created a function which will make the Ajax request.
/**
* Onload, set up dismiss events. Vanilla JS.
*/
document.addEventListener('readystatechange', function() {
if (document.readyState !== "complete") {
return;
}
// Set up function to make ajax call.
function dismissNotice( noticeElement ) {
// do fetch request to ajax endpoint.
fetch( ajaxurl, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
},
body: 'action=dlx_ratings_nag_dismiss&_ajax_nonce=' + dlxRatingsNag.nonce,
} )
.then( function( response ) {
return response.json();
} )
.then( function( json ) {
if ( json.success ) {
// Hide the notice.
noticeElement.style.display = 'none';
}
} );
}
} );
Code language: JavaScript (javascript)
There’s a JavaScript variable named dlxRatingsNag
that needs to be defined so that the JavaScript doesn’t error out. I’ll be using WordPress function wp_localize_script
to define this variable and also add it the nonce
property.
/**
* Enqueue scripts.
*
* @return void
*/
function enqueue_scripts() {
$screen = get_current_screen();
if ( 'settings_page_dlx-ratings-nag' !== $screen->id ) {
return;
}
wp_enqueue_script(
'dlx-ratings-nag',
plugins_url( 'js/ratings-dismiss.js', __FILE__ ),
array(),
'0.0.1',
true
);
wp_localize_script(
'dlx-ratings-nag',
'dlxRatingsNag',
array(
'nonce' => wp_create_nonce( 'dlx-ratings-nag' ),
)
);
}
Code language: PHP (php)
Next we’ll add in the events for the links and buttons in JavaScript.
// Set up button events.
var ratingNotice = document.querySelector( '.notice-rating' );
if ( null !== ratingNotice ) {
// Get the yes button.
var yesButton = ratingNotice.querySelector( '.yes-button' );
// Set up event.
yesButton.addEventListener( 'click', function( event ) {
dismissNotice( ratingNotice );
} );
// Get the no button.
var noButton = ratingNotice.querySelector( '.dismissable-button' );
// Set up event.
noButton.addEventListener( 'click', function( event ) {
event.preventDefault();
// Hide the notice.
dismissNotice( ratingNotice );
} );
// Get the dismiss button.
var dismissButton = ratingNotice.querySelector( '.notice-dismiss' );
// Set up event.
dismissButton.addEventListener( 'click', function( event ) {
event.preventDefault();
// Hide the notice.
dismissNotice( ratingNotice );
} );
}
Code language: JavaScript (javascript)
If all is well, the rating is now dismissable. The “Yes” button will bring up the ratings screen, and dismiss behind the scenes via Ajax. The “No” and dismiss button will do an Ajax call and hide the notice on the same screen.

Hiding the ratings nag when user meta is set
With the ratings nag now dismissable, we’ll need to read in the user meta and make sure not to show the ratings nag if it is present.
I’ll add this to the ratings_nag
function.
// Get the current user ID.
$user_id = get_current_user_id();
$ratings_nag_dismissed = (bool) get_user_meta( $user_id, 'dlx_ratings_nag_dismissed', true );
if ( true === $ratings_nag_dismissed ) {
return;
}
Code language: PHP (php)
Here is the full ratings_nag
function.
// Register an admin notice callback.
add_action( 'admin_notices', __NAMESPACE__ . '\ratings_nag' );
/**
* Display the ratings nag.
*/
function ratings_nag() {
// Skip ratings nag if WP_DEBUG is enabled.
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
return false;
}
// Get the current user ID.
$user_id = get_current_user_id();
$ratings_nag_dismissed = (bool) get_user_meta( $user_id, 'dlx_ratings_nag_dismissed', true );
if ( true === $ratings_nag_dismissed ) {
return;
}
$screen = get_current_screen();
if ( 'settings_page_dlx-ratings-nag' !== $screen->id ) {
return;
}
// Get the install date. Exit if it doesn't exist.
$install_date = get_option( 'dlx_ratings_nag_install_date', false );
if ( ! $install_date ) {
return;
}
// Get the number of days since the plugin was installed.
$days_installed = round( ( time() - $install_date ) / DAY_IN_SECONDS );
// If less than 14 days, exit.
if ( $days_installed < 14 ) {
return;
}
// Display the ratings nag.
?>
<div class="notice notice-info notice-rating is-dismissible">
<p>
<?php
printf(
/* translators: %s: Plugin name */
esc_html__( 'You have been using %1$s for %2$d days. Would you like to leave a review?', 'dlx-ratings-nag' ),
'<strong>' . esc_html__( 'DLX Ratings Nag', 'dlx-ratings-nag' ) . '</strong>',
$days_installed
);
?>
</p>
<p>
<a href="https://wordpress.org/support/plugin/dlx-ratings-nag/reviews/#new-post" class="button button-primary yes-button" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'Yes, I\'d love to!', 'dlx-ratings-nag' ); ?>
</a>
<a href="<?php echo esc_url( admin_url( 'options-general.php?page=dlx-ratings-nag' ) ); ?>" class="button button-secondary dismissable-button">
<?php esc_html_e( 'No, thanks.', 'dlx-ratings-nag' ); ?>
</a>
</p>
</div>
<?php
}
Code language: PHP (php)
Conclusion
In this tutorial, I demonstrated how to add a ratings notice to your admin panel when developing a plugin. The rating follows .org guidelines, is dismissable, and is time-based.
View the Code on GitHub
The code demonstrated in this tutorial is available on GitHub as a downloadable plugin.
If you have any questions, please leave a comment below. Thank you for reading.
Like this tutorial? There's more like it. Subscribe today!

Ronald Huereca founded DLX Plugins in 2022 with the goal of providing deluxe plugins available for download. Find out more about DLX Plugins, check out some tutorials, and check out our plugins.