Options Pages in Multisite

Step 1. Where in Menu you would like to Display your Settings Page?

You can place the link to your settings page anywhere in Network Dashboard menu, to create a parent menu link, we will use add_menu_page() function, to create a child one – add_submenu_page().


Custom settings pages in Network Dashboard menu
I added one settings page under Themes menu item and another settings page just at the very end of the menu.

Let’s add both of these links:

add_action("network_admin_menu", "misha_new_menu_items");
function misha_new_menu_items() {

		'themes.php', // Parent element
		'Settings Page 1', // Text in browser title bar
		'Settings Page 1', // Text to be displayed in the menu.
		'manage_options', // Capability
		'settings-page-1', // Page slug, will be displayed in URL
		'misha_settings_page_1' // Callback function which displays the page

		'Settings Page 2',
		'Settings Page 2',
 		'', // Icon
		100 // Position of the menu item in the menu.


A couple words where to insert the code – the best way is to create your custom plugin and activate it for the Network.

Line 5 – for submenu elements you can specify the Parent item:

Line 21 – for Parent elements play with the position parameter (in the example it is equal to 100), the less the number, the higher your menu item will be displayed in menu.

Custom Icon

Ok, but what if I don’t want the default cog icon to be displayed for my custom settings pages?

There are two ways to change the icon, the first and the simplest one is to use the build-in Dashicons icons set. If you want to use it follow these steps:


Custom menu icon in network dashboard.

The second way may be helpful if there is no icon in Dashicons set which you need, maybe it is your custom plugin logo. In this method you have to include your own icon set in admin and add some custom CSS, example:

add_action( 'admin_head', 'misha_custom_admin_icons' );
function misha_custom_admin_icons(){
	echo '<style>

	 you can include your custom icon sets with @font-face here


	#toplevel_page_settings-page-2 .wp-menu-image:before {
		font-family: "your custom icon font name";
		content: "\e909";

As you can see on line 11, part of the element ID attribute is our page slug, #toplevel_page_{SLUG}.

Step 2. Settings Fields

As you remember from Step 1, there should be callback functions misha_settings_page_1() and misha_settings_page_2() to print the page content and option fields.

I’m not going to create both of these options pages, one of them will be enough to show you how it works.

function misha_settings_page_1() {

	echo '<div class="wrap">
		<h1>Theme Options</h1>
		<form method="post" action="edit.php?action=mishaaction">';
			wp_nonce_field( 'misha-validate' );
			echo '
			<h2>Section 1</h2>
			<table class="form-table">
					<th scope="row"><label for="some_field">Some option</label></th>
						<input name="some_field" class="regular-text" type="text" id="some_field" value="' . esc_attr( get_site_option( 'some_field') ) . '" />
						<p class="description">Field description can be added here.</p>
			<h2>Section 2</h2>
			<table class="form-table">
					<th scope="row">Some checkbox</th>
					<td><label><input name="some_checkbox" type="checkbox" value="yes" ' . checked('yes', get_site_option( 'some_checkbox'), false ) . '> Yes, check this checkbox</label></td>
		echo '</form></div>';


A couple words about functions:

Why we do not use Settings API here

I saw some examples of using Settings API for Multisite Options Pages and I couldn’t understand what was the reason of using it, because in those examples there was another hook for saving the option fields.

The reason why we usually use Settings API is because we do not have to save option values ourselves! Settings API do it instead. Isn’t it right?

Step 3. Save Fields

Here is how I do it:

So, let’s go 🚀

add_action( 'network_admin_edit_mishaaction', 'misha_save_settings' );

function misha_save_settings(){

	check_admin_referer( 'misha-validate' ); // Nonce security check

	update_site_option( 'some_field', $_POST['some_field'] );
	update_site_option( 'some_checkbox', $_POST['some_checkbox'] );

	wp_redirect( add_query_arg( array(
		'page' => 'settings-page-1',
		'updated' => true ), network_admin_url('themes.php')



Some significant things you have to keep in mind:

  1. Nonce field parameter misha-validate on line 5 and in Step 2, line 6 must match.
  2. And it is obvious, that field names must be appropriated too, but you can use different input field names and option settings names – it is OK.
  3. Set the redirect correctly, the new URL should be like this: {YOU SETTINGS PAGE}&updated=true. In the example I added my page under Themes menu item, so, on line 12 I have network_admin_url('themes.php'), but if you added it under Users menu, this value will be changed to network_admin_url('users.php') etc. And do not forget to make sure that page parameter on line 11 matches the slug of your settings page! 😁

Step 4. Notices

And welcome to the most simple step now! Your settings page should work perfectly by this moment. But we have to display some notices for better user experience, haven’t we?

add_action( 'network_admin_notices', 'misha_custom_notices' );

function misha_custom_notices(){

	if( isset($_GET['page']) && $_GET['page'] == 'settings-page-1' && isset( $_GET['updated'] )  ) {
		echo '<div id="message" class="updated notice is-dismissible"><p>Settings updated. You\'re the best!</p><button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button></div>';


The only thing you should care about here is that page slug settings-page-1 on line 5 matches the actual page slug.

And finally, that’s how my page looks like:

Creating custom options pages in WordPress Network Dashboard
Misha Rudrastyh

Misha Rudrastyh

I develop websites since 2008, so it is total of 13 years of experience, oh my gosh. Most of all I love love love to create websites with WordPress and Gutenberg, some ideas and thoughts I share throughout my blog.

Need some developer help? Contact me