Add a CSS Class to wp_nav_menu Links in WordPress

wp_nav_menu() in WordPress lets you set classes on the menu <ul> and, from the Menus screen, on each <li>, but there is no argument for a class on the link itself. With Tailwind CSS that is exactly where the classes belong; otherwise you end up targeting links from the parent with selectors like [&_a]:px-4.

This snippet adds an add_a_class argument to wp_nav_menu(). Every <a> in that menu gets the classes you pass, and other menus on the page are left alone.

The snippet

<?php
/**
 * Add CSS classes to the <a> links of wp_nav_menu() with an extra argument:
 * wp_nav_menu( array( 'theme_location' => 'primary', 'add_a_class' => 'px-4 py-2' ) );
 */
add_filter( 'nav_menu_link_attributes', 'dpc_nav_menu_link_class', 10, 3 );

function dpc_nav_menu_link_class( $atts, $item, $args ) {
	if ( ! is_object( $args ) || empty( $args->add_a_class ) || ! is_string( $args->add_a_class ) ) {
		return $atts;
	}

	// Keep classes other code already added, then append ours without duplicates.
	$classes = array_merge(
		preg_split( '/\s+/', (string) ( $atts['class'] ?? '' ), -1, PREG_SPLIT_NO_EMPTY ),
		preg_split( '/\s+/', $args->add_a_class, -1, PREG_SPLIT_NO_EMPTY )
	);

	$atts['class'] = implode( ' ', array_unique( $classes ) );

	return $atts;
}

Use it

Pass add_a_class wherever your theme, a template part or a page builder’s PHP element calls wp_nav_menu():

<?php
wp_nav_menu(
	array(
		'theme_location' => 'my-footer-menu',
		'menu_class'     => 'footer-top list-unstyled',
		'add_a_class'    => 'block px-4 py-2 text-black hover:bg-slate-200',
	)
);

Add it with Scripts Organizer

In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Everywhere, tick PHP under Script location, paste the snippet into the PHP editor and publish. Leave Action/Hook empty.

Paste only the filter, not the wp_nav_menu() call. The filter does nothing until a menu is rendered, so it is safe to load early. The call belongs in a template, where the menu should appear; at load time WordPress has not run the query yet, and the menu would be printed before the page starts.

The same filter also works unchanged in a small plugin, an mu-plugin or functions.php.

How it works

  • wp_nav_menu() keeps unknown arguments and passes the whole argument object to its filters. nav_menu_link_attributes runs for each link with that object as $args, so the snippet can read $args->add_a_class.
  • Menus called without add_a_class return their attributes unchanged.
  • Classes that a theme or plugin already put on the link are kept, and yours are appended. The older version of this post replaced them.
  • The value is not run through sanitize_html_class(), because that strips the : and [] that Tailwind classes use. WordPress escapes every link attribute with esc_attr() when it builds the <a> tag, so a stray quote cannot break out of the attribute.

Customise it

  • Top-level links only: add a fourth parameter $depth = 0 to the function, change 10, 3 to 10, 4, and return $atts early when $depth > 0.
  • Classes on the <li> instead: the same pattern works with the nav_menu_css_class filter, which receives the same $args object as its third argument.
  • Classes for one menu item only: use the CSS Classes field in Appearance → Menus (enable it under Screen Options). Those go on the <li>.

Gotchas

  • Block themes use the Navigation block, which does not call wp_nav_menu(). This snippet only affects classic menus.
  • Tailwind only generates classes it finds in the files it scans. If your build does not scan the template with the wp_nav_menu() call, add that file to its content paths or safelist the classes. Prefer to compile Tailwind inside WordPress? Winden does that on the server.

In the Scripts Organizer library Import it with one click from Scripts Organizer.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You’ll also get DPlugins product updates. No spam, and you can unsubscribe anytime.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You'll also get DPlugins product updates. No spam, and you can unsubscribe anytime.