What is the best way to model a nested menu?

I’m trying to move our menu hierarchy/taxonomy into DatoCMS but am struggling with how to best model this in the CMS. For example, if my site is organized like:

  • Home
  • Blog Posts
    • Announcements
    • Release Notes
  • Products
    • Shirts
    • Snakes
    • Teapots
      • Ceramic
      • Steel
      • Glass

What’s the best way to make a drag-and-drop UI for editors to be able to edit the names and links of each item? I was hoping to find something like Wordpress’s menu editor. Is there anything like that in DatoCMS, or modelable to make it similar?

I know you can drag and drop things around the editor UI tree to reorganize pages and content models. But if I do that there, can I expose the result via API somehow?

Or is there a better way to do this?

I tried this out as a Modular Content Field, but it doesn’t support proper nesting. I thought about using two different types of blocks (entries vs section headers) but that would only support one level of nesting.

Maybe a better approach would be to make a JSON field with a plugin that uses something like react-sortable-tree?

I’ll try implementing that as a plugin if there isn’t a better built-in way.

At the field level it’s not supported. But you can make a model that is hierarchal if that suits your needs:

1 Like

@roger does the tree-like collection solve your issue?

@mvandiest and @mat_jack1, thank you for that suggestion.

Edit: Please ignore this post and go to the next one. I misunderstood “tree-like collection” as “nested model”, but they are not the same thing. See next post. Sorry!

Edit: My bad, I totally missed the “as a tree” option! That should work. I tried it out and it seems to work, but it’s still a bit confusing in terms of being able to drag things around in the hierarchy like in the video above. If you wanted to move one node under another, it would take several different operations (editing the child, then the grandchild, then deleting the great-grandchild node, then recreating that same structure in another great-grandparent). It’s messy.

On the query side, graphQL also doesn’t seem to allow recursive lookups, so to get all the levels of nested menus, it requires a crazy query like:

query MyQuery {
  allMenus {
    reference {
      ... on MenuRecord {
        id
        title
        reference {
          ... on MenuRecord {
            id
            title
            reference {
              ... on MenuRecord {
                id
                title
                reference {
                  ... on MenuRecord {
                    id
                    title
                    reference {
                      ... on MenuRecord {
                        id
                        title
                        reference {
                          ... on MenuRecord {
                            id
                            title
                            reference {
                              ... on MenuRecord {
                                id
                                reference {
                                  ... on MenuRecord {
                                    id
                                  }
                                }
                                title
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    title
  }
}

I’ll keep exploring both options (plugin field vs nested models). Thanks for the feedback!

I am sorry! I misunderstood previously.

I recreated the model as a tree-like collection instead of a nested one (I thought they were the same, I was wrong).

So now it looks good on the editor side:
image

But how about on the query? How do I recreate that structure in the content delivery API? The query response still looks flat :frowning:

Hey @roger,

Tree-like models have special children, parent and position fields. You will have to allow for your max depth as I do below by nesting more children.

Side note: I am not sure why the Tree-Like fields aren’t in your GQL Explorer screenshot, maybe you need to refresh to see them. But for any Tree-Like model you should have these special fields to work with.

My example:

allGlobalHeaderNavigations(
    filter: { parent: { exists: "*" } }
    orderBy: position_ASC
  ) {
    title
    url
    position
    children {
      url
      title
      position
      children {
        title
        url
        position
      }
    }
  }
1 Like

@mvandiest Thank you! That was exactly it. I kept looking for children/parent fields and couldn’t find them… just needed a refresh :slight_smile:

There we go. Problem solved, thanks all!

2 Likes