Highlighting Features with WordPress Admin Pointers – A Quick Guide for Plugin Authors

Here’s a quick intro to admin pointers for plugins to WordPress 3.3+, based on some work I recently put into making the help system in Networks more intuitive:

Step One — Add the admin pointer style and script to your page

This is a fairly standard chain of admin_menu action functions, which should look familiar if you’re using a class for your plugin. If they don’t, some light background reading may be in order.

You’ll need to create a script file for your admin page to follow this guide, although you could also dump the required JavaScript into the admin page itself.

function admin_menu()
	$this->admin_page = add_menu_page( __('My Page'), __('My Page'), 'manage_options', 'my-page', FUNCTION );
	wp_register_script( 'my_admin_js', plugins_url(SCRIPT FILE, __FILE__), array('jquery') );
	add_action( 'admin_print_scripts-' . $this->admin_page, array( &$this, 'add_admin_scripts' ) );
	add_action( 'admin_print_styles-' . $this->admin_page, array( &$this, 'add_admin_styles' ) );
function add_admin_styles() {
	wp_enqueue_style( 'wp-pointer' );
function add_admin_scripts() {
	wp_enqueue_script( 'wp-pointer', false, array('jquery') );
	wp_enqueue_script( 'my_admin_js', false, array('jquery') );
	wp_localize_script( 'my_admin_js', 'strings', $this->localize_admin_js() );

In addition to enqueueing the admin pointer CSS and script files, we’ve set up a localization function. Beside being good coding practice, generating the strings for the admin pointer here, instead of in the script file, is going to allow us to use a nice shortcut.

Step Two — Build the pointer text and check for dismissal

function localize_admin_js() {
	$pointer_text = '<h3>' . esc_js( __( 'My Pointer Header') ) . '</h3>';
	$pointer_text .= '<p>' . esc_js( __( 'This is the text of my pointer.' ) ). '</p>';
	// Get the list of dismissed pointers for the user
	$dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
	// Check whether our pointer has been dismissed
	if ( in_array( POINTER HANDLE, $dismissed ) ) {
		$pointer_text = '';
	return array(
		'pointerText' => $pointer_text

In testing, setting the pointer text to '' was sufficient to hide the pointer from the screen. However, we can add another layer of protection in the script, which you’ll see below.

Step Three – Script the pointer object gracefully

If you don’t already have an admin script file, you’ll need to create one for this step.

jQuery(document).ready( function() {
	/** Check that pointer support exists AND that text is not empty */
	if(typeof(jQuery().pointer) != 'undefined' && strings.pointerText != '') {
			content    : strings.pointerText,
			close  : function() {
				jQuery.post( ajaxurl, {
					pointer: POINTER HANDLE,
					action: 'dismiss-wp-pointer'

Note the close property of the pointer object: calling this action hooks us right into WordPress’s pointer management, so that when the user clicks the Dismiss button, our pointer is added to his list of dismissed pointers. The wp-pointer stylesheet takes care of making the pointer look right so long as there’s an <h3> and one or more paragraphs, and the close button is added and wired up automatically.

Hope this helps anyone wishing to use this neat, new feature!

2 Responses

  1. hassanz says:

    thank you. One problem: pointer reappears on page refresh, after click dismiss.

    • ddean says:

      Hi hassanz –

      Make sure the AJAX request that fires when you click dismiss is completing successfully. It sounds like something on your install may be getting in the way.

      – David

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>