WordPress Admin Menu Items with Custom URLs

This tutorial adds custom menu items to the WordPress admin menu, with whatever URLs you want. It works great with our tutorial on creating custom CSV exports from WordPress, and is useful if you want to add things like a “Get Support” link to a client’s site.

We need to get a bit hacky with this one because WordPress doesn’t make it easy to create admin menu items with custom URLs. The add_menu_page() function will create a placeholder menu item for us. Then, we’ll add some JavaScript magic to find the placeholder item, create & append the custom menu items and, finally, remove the placeholder item from the menu.

There’s not much code to write, so let’s jump right in.

Placeholder for a custom admin menu item
Placeholder menu item
Custom WordPress admin menu items
Admin menu items with custom URLs

infoMake sure you’re using a custom child theme because we’re going to edit functions.php.

The Back-end Code

In your custom child theme’s folder, create a file called “wpt-custom-admin-menu-items.php” and a subfolder called “wpt-custom-admin-menu-items”. Go into “wpt-custom-admin-menu-items” and create an empty file called “wptcami-admin.js”.

Edit wpt-custom-admin-menu-items.php and paste the following into it:

<?php

/**
 * WP Tutorials : Custom Admin Menu Items (WPTCAMI)
 *
 * https://wp-tutorials.tech/refine-wordpress/wordpress-admin-menu-items-with-urls/
 */

defined('WPINC') || die();

// Set this to false to remove the demo URLs.
const WPTCAMI_INCLUDE_TEST_DATA = true;

const WPTCAMI_USER_CAPABILITY = 'administrator';
const WPTCAMI_MENU_POSITION = 100;
const WPTCAMI_MENU_ITEM_ICON = 'dashicons-media-spreadsheet';

/**
 * Return an array of link meta data, where each menu item is a little array
 * with a URL and and some link text.
 */
function wptcami_get_menu_items() {
	global $wptcami_menu_items;

	if (is_null($wptcami_menu_items)) {
		$wptcami_menu_items = array();

		if (WPTCAMI_INCLUDE_TEST_DATA) {
			$wptcami_menu_items[] = array(
				'url' => 'https://google.com/',
				'text' => 'Google',
			);

			$wptcami_menu_items[] = array(
				'url' => 'https://bing.com/',
				'text' => 'Bing',
			);
		}

		// Hook the wptcami_menu_items filter in your own code to define
		// your menu items.
		$wptcami_menu_items = (array) apply_filters('wptcami_menu_items', $wptcami_menu_items);
	}

	return $wptcami_menu_items;
}

/**
 * Do we have any custom admin menu items defined?
 */
function wptcami_are_there_menu_items() {
	return !empty(wptcami_get_menu_items());
}

/**
 * If there are any custom admin menu items, add our placeholder menu item to
 * the WP admin menu.
 */
function wptcami_add_stub_menu_item($context) {
	if (wptcami_are_there_menu_items()) {
		add_menu_page(
			'WPT CAMI Page', // Not used
			'WPT CAMI Menu', // Change this text if you want
			WPTCAMI_USER_CAPABILITY,
			'wptcami-items',
			null,
			WPTCAMI_MENU_ITEM_ICON,
			WPTCAMI_MENU_POSITION
		);
	}
}
add_action('admin_menu', 'wptcami_add_stub_menu_item');

/**
 * If there are any custom admin menu items, enqueue our admin JavaScript file
 * and pass the menu items to it in the global JS variable, wptcamiData
 */
function wptcami_enqueue_admin_assets() {
	if (wptcami_are_there_menu_items()) {
		$base_url = get_stylesheet_directory_uri();
		$version = wp_get_theme()->get('Version');

		$handle = 'wptcami-admin';

		wp_enqueue_script(
			$handle,
			$base_url . '/wpt-custom-admin-menu-items/wptcami-admin.js',
			array('jquery'), // Our JS code depends on jQuery.
			$version
		);

		wp_localize_script(
			$handle,
			'wptcamiData',
			array(
				'stubMenuItemSelector' => '#adminmenu li a.toplevel_page_wptcami-items',
				'menuItems' => wptcami_get_menu_items(),
			)
		);
	}
}
add_action('admin_enqueue_scripts', 'wptcami_enqueue_admin_assets');

Now open your child theme’s functions.php, add the following two lines and save the changes:

// Custom links in the WP Admin menu (WPTCAMI)
require_once dirname(__FILE__) . '/wpt-custom-admin-menu-items.php';

Our core function is wptcami_get_menu_items(), which returns an array of menu items. The critical thing in here is that we trigger a custom filter called “wptcami_menu_items”, so we can add custom admin menu items from other code modules, without needing to edit “wpt-custom-admin-menu-items.php” again. But… if you want to, you can just change the contents of this function and add your menu items directly.

Have a look at the constants at the top of the file too. Use WPTCAMI_MENU_POSITION to position the custom items in the admin menu, with lower numbers placing the items towards the top of the menu, and higher numbers towards the bottom.

Save your changes, then go to your site’s admin area. You should see the placeholder menu item in the left-hand admin menu. If you click on it, you’ll get a “404 Not Found” because we’ve not set up a render-callback… and we’re not going to either, because we’re using full URLs in our links.

Swap Out the Placeholder Menu Item for our Menu Items

Open “wpt-custom-admin-menu-items/wptcami-admin.js” and paste the following into it:

/**
 * wptcami-admin.js
 * 
 * https://wp-tutorials.tech/refine-wordpress/wordpress-admin-menu-items-with-urls/
 */
(function($) {
	'use strict';
	$(window).on('load', function() {
		// Uncomment this to check that the script is executing.
		// console.log('WPT CAMI Load');

		if (typeof wptcamiData !== 'undefined') {
			// Uncomment this to check that the script is initialising.
			// console.log(`WPT CAMI Init : ${wptcamiData.stubMenuItemSelector}`);

			const stubMenuItemLink = $(wptcamiData.stubMenuItemSelector);
			const stubMenuItemListItem = $(stubMenuItemLink).closest('li');
			const iconElement = $(stubMenuItemListItem).find('.wp-menu-image').first();

			if (stubMenuItemListItem.length === 1) {
				let previousListItem = stubMenuItemListItem;
				let itemIndex = 0;

				// Loop through our custom menu items.
				wptcamiData.menuItems.forEach((menuItem) => {
					const newItem = $(`
<li id="toplevel_page_wptcami-item-${itemIndex}" class="wp-not-current-submenu menu-top toplevel_page_wptcami-item-${itemIndex}" style="display:none;">
	<a href="${menuItem.url}" class="wp-not-current-submenu menu-top toplevel_page_wptcami-item-${itemIndex}">
		<div class="wp-menu-name">${menuItem.text}</div>
	</a>
</li>
`);
					// Clone the placeholder's icon
					iconElement.clone().insertBefore(newItem.find('.wp-menu-name'));

					// Add our new DOM element and fade it in.
					newItem.insertAfter(previousListItem);
					newItem.fadeIn();

					previousListItem = newItem;

					++itemIndex;
				});

				$(stubMenuItemListItem).remove();
			}
		}
	});
})(jQuery);

The logic breaks down like this:

  1. If wptcamiData has been passed from the back-end using wp_localize_script(), then…
    1. Get a handle to our placeholder li list item
    2. For each of our custom menu items in wptcamiData.menuItems
      1. Create a new list item with a link and a dashicon
      2. Append the new list item to the DOM immediately after our placeholder
    3. After all the menu item elements have been added, remove the original placeholder list item

Save that and reload your admin area. You should see the placeholder shows for a moment, and then it’s replaced by the two demo menu items (Google and Bing).

Adding Your Own Menu Items

Although you can add menu items by modifying wptcami_get_menu_items(), it’s a bit “cleaner” to hook our wptcami_menu_items filter. Here’s how you could hook the filter in your child theme’s functions.php file:

function custom_csv_wptcami_menu_items($menu_items) {
	$base_uri = get_stylesheet_directory_uri();

	$menu_items[] = array(
		'url' => $base_uri . '/custom-csv-exports/report-one.php',
		'text' => 'My Report One',
	);

	$menu_items[] = array(
		'url' => $base_uri . '/custom-csv-exports/report-two.php',
		'text' => 'My Report Two',
	);

	return $menu_items;
}
add_filter('wptcami_menu_items', 'custom_csv_wptcami_menu_items');

infoDon’t forget to go back into “wpt-custom-admin-menu-items.php” and set WPTCAMI_INCLUDE_TEST_DATA = false near the top of the file.

There you have it – happy admin menu-hacking 😎 👍

Like This Tutorial?

loading

Let us know

Leave a Comment

Your email address will not be published.