Here is a small thing that catches almost everyone the first time, and it is not really their fault, because the tooling sets them up for it.
You want a copy of a navigation menu. Maybe for a redesign, maybe a seasonal variant, maybe a backup before you start deleting items. So you open Appearance > Menus and look for a Duplicate button.
There isn't one. WordPress core has never had a way to copy a menu.
So you do the obvious thing and rebuild it by hand in a new menu, dragging items in one at a time. It works, until you look at the dropdowns and every nested item is now sitting at the top level. No error. Just a flat menu where your hierarchy used to be. Let me explain why, because once you see it you will never be confused by it again.
What a WordPress menu actually is
A menu is not a block of text. It is two kinds of database records working together.
- The menu itself is a taxonomy term, in the
nav_menutaxonomy. - Every item inside the menu is a separate hidden post, of type
nav_menu_item, each with its own post ID.
The label, the URL, the link target, all of it lives in that item's postmeta. See wp_get_nav_menu_items() for how WordPress reads them back out.
Where the nesting lives, and why it breaks
The part that makes a menu a menu, the nesting, is stored as postmeta on each item:
_menu_item_menu_item_parent = 128 // "my parent is menu item #128"
That is the trap. The parent is saved as a menu item ID, not a name or a slug. So the whole hierarchy is a web of items pointing at each other by ID.
Now rebuild that menu by hand. Every item you recreate is a brand new post with a brand new ID. The old parent values still say things like 128, but item 128 no longer exists in the new menu. WordPress cannot resolve the reference, so it treats the child as a top-level item. Do that across a nested menu and the entire thing flattens.
This is the core of it: duplicating a menu is not copying text, it is recreating a set of posts AND remapping the parent pointers between them. Copy-paste does not do the second part. Naive export/import does not do the second part. That is why they lose your dropdowns.
Doing it correctly in code
If you were to script it, the shape is: create the new menu, then create each item with wp_update_nav_menu_item(), keep a map of old item ID to new item ID as you go, and set each new item's menu-item-parent-id to the mapped new ID rather than the old one.
// Pseudocode for the part that matters
$id_map = [];
foreach ($original_items as $item) {
$new_id = wp_update_nav_menu_item($new_menu_id, 0, [
'menu-item-title' => $item->title,
'menu-item-url' => $item->url,
'menu-item-status' => 'publish',
// do NOT set the old parent here yet
]);
$id_map[$item->ID] = $new_id;
}
// Second pass: now that every item exists, fix the parents
foreach ($original_items as $item) {
if ($item->menu_item_parent) {
update_post_meta(
$id_map[$item->ID],
'_menu_item_menu_item_parent',
$id_map[$item->menu_item_parent] // remapped, not the old ID
);
}
}
Two passes, because you cannot set a parent to an ID that does not exist yet. This remap is exactly the work a one-click duplicator does for you.
Gotcha one: the copy is not assigned to a location
Even done correctly, a duplicated menu displays nowhere. It is not attached to any theme location. You assign it under Appearance > Menus > Manage Locations when you are ready.
That is not a missing step, it is the point. When you clone a menu to redesign it, you want the copy invisible to visitors until it is finished. Duplicate, rework the copy, swap the location at the end, and the live nav never breaks while you work.
Gotcha two: the copy is a snapshot, not a mirror
Once you duplicate, the two menus are independent sets of nav_menu_item posts. Editing the original later does not change the copy. Perfect for a backup taken before a risky edit. Not what you want if you assumed the two would stay in sync.
The settings-panel version
I did this run with WP Adminify's Productivity module, which adds the one-click duplicate right where you already are. 30 second demo below.
What one click gets you, and what it does not:
- A full copy of the menu: every item, the exact order, and the nested dropdowns, with the parent IDs remapped so the hierarchy survives
- The copy left unassigned, so it shows nowhere until you assign a location (the safe-redesign default)
- What it does NOT do: keep the copy in sync with the original. It is a snapshot. Two separate menus from the moment you click
- What to watch: orphaned items pointing at deleted pages get copied too, so tidy the menu around the clone
What changed after enabling Menu Duplicator in Adminify > Productivity:
- A Duplicate Menu button appeared on the Appearance > Menus screen, next to the menu name, no code to add it
- One click produced a
(Copy)of a three-level menu with the nesting intact, which is the part hand rebuilds ruin - It sits alongside the rest of the same module, the dashboard widget cleanup and the admin notice hiding, so the admin you are working in is also the admin you cleaned
- Pair it with the admin menu layout options if what you actually wanted was to reorder the WordPress admin sidebar, which is a different feature people confuse with navigation menus
The caveat I will not skip: duplication is a copy, not a link. The moment you click, you have two menus that drift apart independently. That is ideal for backups and variants, and wrong if you expected a live mirror. Match the tool to the intent.
Step-by-step doc: One-click menu duplication in WordPress
More admin control in the same direction: remove unwanted dashboard widgets, hide admin notices, change the admin menu layout and width, and hide Screen Options and the Help tab.
So: do you duplicate menus with a tool, rebuild and re-nest them by hand, or spin up staging for every nav change?
Top comments (0)