Where does Wordpress store custom menus?

I had to copy a whole wordpress-installation to another server. I simply installed a new Wordpress there and imported all the data from an xml-file previous Wordpress "exported" for me. Anyway, custom menus were not included. Do you know where in the database are they located, so I could grab them from there?


This setting happens in the wp_posts table. Look in the table for records where the menu_order greater than zero.

select * from wp_posts where menu_order > 0;

It will also give you the name of the option in the wp_options table where the menu option is configured.

select * from wp_options where option_name = "nav_menu_options";

Also be aware that that wordpress import/export tool will not import media (images,video etc) from the media library which are not being used in posts. If you've got stuff that you directly linked to, its not going to be moved either.

I've got a site with a Magento install and WordPress install sitting next to each other, and cross-linking.

I recently spent several hours writing a class to drop into the Magento installation so that I could render the WordPress menu as the navigation menu in the Magento site.

Posts here have been helpful, but none of them have completely explained the structure of how WordPress menus are stored. Like many WP things, it is stored in a series of relationships. Here's the structure:

(note that this example presumes a table prefix if of "wp_")

  1. First, it's important to recognize that a menu item can be a post (technically it's a page, but pages are stored in the post table), a category, or it can be a custom link.
  2. Because WP supports multiple menus, you first look in the wp_term_taxonomy table table to find any terms with the taxonomy of 'nav_menu'. Note the term_id from that table.
  3. To find the name and slug of the menu, visit the wp_terms table and find the term with the id noted from step 2 above.
  4. Go to wp_term_relationships table and list all of the records with the term_taxonomy_id that matched the term_id from step 1. The object_id field tells you the wp_post.id record where you can find the menu record.
  5. Finally, go to wp_postmeta to find many elements describing the menu. Of particular interest are:
    • _menu_item_object - the TYPE of menu item (page, custom, or category)
    • _menu_item_object_id - the id of the actual POST (or category, if it's a category) that the menu item references
    • _menu_item_menu_item_parent - the heirarchical parent structure of the MENU (which can be different than the post parent relationships)
    • _menu_item_url - the slug of the menu item (if it is a custom link menu item)

Sample SQL statements to perform the above described operation:

SELECT t.term_id 
FROM wp_term_taxonomy as tax 
LEFT JOIN wp_terms as t ON tax.term_id = t.term_id 
WHERE taxonomy = 'nav_menu' and name like '%top%'

(looks for a menu item with the name of 'Top', and gets the term id)

SELECT p.ID, p.post_title, p.post_name, p.menu_order, n.post_name as n_name, n.post_title as n_title, m.meta_value, pp.meta_value as menu_parent
FROM wp_term_relationships as txr 
INNER JOIN wp_posts as p ON txr.object_id = p.ID 
LEFT JOIN wp_postmeta as m ON p.ID = m.post_id 
LEFT JOIN wp_postmeta as pl ON p.ID = pl.post_id AND pl.meta_key = '_menu_item_object_id' 
LEFT JOIN wp_postmeta as pp ON p.ID = pp.post_id AND pp.meta_key = '_menu_item_menu_item_parent' 
LEFT JOIN wp_posts as n ON pl.meta_value = n.ID 
WHERE txr.term_taxonomy_id = 3 AND p.post_status='publish' 
    AND p.post_type = 'nav_menu_item' AND m.meta_key = '_menu_item_url' 
ORDER BY p.menu_order

(loads the data for the menu, based on the term_id of 3)

Note that this sql statement will work for pages and custom menus (I don't have any categories, so didn't include that). The data loaded will allow you to build the permalink using the siteurl from the wp_options table, and appending the post_name to the end (technically, it's not getting the parent structure, but WP finds the page/post properly without it)

Update A commenter asked about assembling the child menu items with the parent menu items. That will need to be done with PHP. Something like below will do that for you:

// run the query from above
$results = $wpdb->get_results('SELECT....');

// declare new variable to store "assembled" menu
$menu = array();

// loop over the items assigning children to parents
foreach( $results AS $row ) {
    // assemble key bits for the menu item
    $item = array(
        // handles custom navigation labels
        'title' => ( $row->post_title ) ? $row->post_title : $row->n_title,
        // handles custom links
        'permalink' => ( $row->meta_value ) ? $row->meta_value : get_permalink( $row->ID ),
        // declares empty placeholder for any child items
        'children' => array()

    // if the menu item has a parent, assign as child of the parent
    if ( $row->menu_parent ) {
        $menu[ $row->menu_parent ][ 'children' ][] = $item;
    } else {
        $menu[ $row->ID ] = $item;

var_dump( $menu );

// outputs something like below:
 * array (size=6)
 *  77 => 
 *     array (size=3)
 *       'title' => string 'About Us' (length=8)
 *       'permalink' => string 'http://www.example.com/about' (length=33)
 *       'children' => 
 *        array (size=7)
 *          0 => 
 *            array (size=3)
 *              'title' => string 'Welcome' (length=22)
 *              'permalink' => string 'http://www.example.com/welcome' (length=35)
 *              'children' => 
 *                array (size=0)
 *                  empty
 *          1 => 
 *            array (size=3)
 *              'title' => string 'Mission' (length=20)
 *              'permalink' => string 'http://www.example.com/mission' (length=33)
 *              'children' => 
 *                array (size=0)
 *                  empty
 *  90 => 
 *    array (size=3)
 *      'title' => string 'Contact Us' (length=10)
 *      'permalink' => string 'http://www.example.com/contact' (length=33)
 *      'children' => 
 *        array (size=5)
 *          0 => 
 *            array (size=3)
 *              'title' => string 'Why Us' (length=12)
 *              'permalink' => string 'http://www.example.com/why' (length=35)
 *              'children' => 
 *                array (size=0)
 *                  empty
 *  1258 => 
 *    array (size=3)
 *      'title' => string 'Login' (length=12)
 *      'permalink' => string 'https://customlink.example.com/some/path/login.php' (length=82)
 *      'children' => 
 *        array (size=0)
 *          empty

I have been searching high and low for the entire structure and I finally cracked the code:

wp_posts AS p
INNER JOIN wp_postmeta AS m ON m.post_id = p.ID
INNER JOIN wp_posts AS md ON md.ID = m.meta_value AND m.meta_value = md.ID
INNER JOIN wp_users ON md.post_author = wp_users.ID
p.menu_order > 0 AND
p.post_type = 'nav_menu_item' AND
m.meta_key = '_menu_item_object_id'
p.menu_order ASC

I found this just because I was looking for the answer myself. I see your post is quite old, but the answer is in wp_postmeta, run this query:

FROM `wp_postmeta`
WHERE meta_key LIKE '%menu%'
LIMIT 0, 30

You'll find many entries.

I added additional column on cale_b's query. The column lvl has depth for indentation.

Assume that:

  • max depth is 6, so i defined 6 variables.
  • menu's post_id is over 6. Most of you would've already exceed that number.


select case when a.meta_value = 0 then least(@lvl:=1, @p1:=a.id) 
            when a.meta_value = @p1 then least(@lvl:=2, @p2:=a.id) 
            when a.meta_value = @p2 then least(@lvl:=3, @p3:=a.id) 
            when a.meta_value = @p3 then least(@lvl:=4, @p4:=a.id) 
            when a.meta_value = @p4 then least(@lvl:=5, @p5:=a.id) 
            when a.meta_value = @p5 then least(@lvl:=6, @p6:=a.id) 
       end lvl
  from (
        SELECT p.id
              ,n.post_name as n_name
              ,n.post_title as n_title
              ,m.meta_value meta2
          FROM wordpress_dw.stat_term_relationships as txr 
               INNER JOIN wordpress_dw.stat_posts as p ON txr.object_id = p.ID 
               LEFT JOIN wordpress_dw.stat_postmeta as m ON p.ID = m.post_id and m.meta_key = '_menu_item_url' 
               LEFT JOIN wordpress_dw.stat_postmeta as pl ON p.ID = pl.post_id AND pl.meta_key = '_menu_item_object_id' 
               LEFT JOIN wordpress_dw.stat_postmeta as pp ON p.ID = pp.post_id AND pp.meta_key = '_menu_item_menu_item_parent' 
               LEFT JOIN wordpress_dw.stat_posts as n ON pl.meta_value = n.ID 
              ,(select @lvl:=0, @p1:=0, @p2:=0, @p3:=0, @p4:=0, @p5:=0, @p6:=0) x
         WHERE txr.term_taxonomy_id = 2  -- your menu term id 
           AND p.post_status='publish'
           AND p.post_type = 'nav_menu_item' 
         ORDER BY p.menu_order  
       ) a       


lvl |id  |
1   |508 |
1   |509 |
2   |510 |
3   |511 |
3   |512 |
3   |513 |
3   |514 |
2   |515 |
1   |516 |
2   |517 |
2   |518 |
3   |519 |
3   |520 |
3   |521 |
3   |522 |

Need Your Help

How can I pass a Class as parameter and return a generic collection in Java?

java generics class polymorphism parameter-passing

I am designing a simple Data Access Object for my Java application. I have a few classes (records) that represents a single row in tables like User and Fruit.

Using "Counter" in Python 3.2

python python-3.x

I've been trying to use the Counter method in Python 3.2 but I'm not sure if I'm using it properly. Any idea why I'm getting the error?