Best practices for organizing website navigation

Hi, I am new to DatoCMS and trying to figure out a good solution for handling a collection of navigation items that would reference single instance models for pages in my website. I have my page models all set up and wanted to either query for the list of Pages itself, or create a model for Menu that would hold references to each of these page models. Can anyone point me in the right direction for best practices in organizing website navigation? Thank you!

Hi @JLernDesign, and welcome to DatoCMS!

You can make a single-link field in your menu_item model. That can link to the other single-instance page models, like this:


Then when you edit a menu item, you can link it to one of the single-instance models:

The dropdown is clearer than the input box right now, because the single-instance models don’t have record titles set up, so they just chose a random text field to use as its name, e.g.:

(You can fix that by giving those single instance models another text field to use as their names. Or just use the dropdown.)

In any case, once you have that set up, you can then query menu_items for all the single-instance models they link to, like this:

query MyQuery {
  allMenuItems {
    id
    title
    slug
    model {
      ... on AboutRecord {
        id
        _modelApiKey
      }
      ... on ExpertiseRecord {
        id
				_modelApiKey
      }
      ... on PsilocybinRecord {
        id
        _modelApiKey
      }
      ... on ServicesPageRecord {
        id
        _modelApiKey
      }
    }
  }
}

I forked your project into a sandbox environment so you can take a look as an example. I’ll DM you the link in a moment here.


However, I should note that this is a somewhat unusual way to compose schema for multiple pages. Single-instance models might make sense for really unusual pages (like a contact form) , but you don’t necessarily need a separate model type for every informational page.

As a hypothetical example: About, Expertise, Services, Psilocybin, could all potentially be records under one “Page” model. That “Page” model can have a Structured Text field and/or Modular Content Fields. Then you use Blocks to make reusable sections, like a “Quote” block that contains text and byline, a pricing block, etc. The different pages can then pick and choose the blocks they want to use in order to compose the final layout.

That way, your schema can be a lot simpler (just one model for all your pages, with reusable blocks). And your frontend template would be reusable across pages, since it would know how to render each block type as a component and build up from there.

The benefit of this approach is that it’s more maintainable long-term (easy to change page layouts both in the CMS and in the frontend, easy to add new blocks as components, etc.). The downside is that it might require a bit more upfront setup since you can’t just hardcode model fields directly into each frontend page.

Up to you which you prefer, but that’s what we see most often (and it makes sense for most websites).

Your way is fine too, it just requires a little more manual linking in the CMS and query, since single-instance models have no inherent relationship to each other. They’re just independent things. In fact, if you want, you can just hardcode their IDs into your frontend nav and not have to retrieve them from GraphQL at all, since technically they are separate models and templates anyway.

@roger Thanks so much for the thorough explanation! I was trying to figure out how I could make all pages use a single Page model even if they all contained different sections and the approach you mentioned sounds a lot cleaner so I’ll give that a try.

My main question if I were to do it that way is how can I prevent my client from using a block that does not belong on a certain page? So for example if I have a block for credentials on the About page and its included as a modular block, is there a way to restrict its use on certain instances of the Page model?

Unfortunately, I don’t think that’s possible :frowning: But is that strictly necessary? What if they change their mind someday and decide to add a credential block elsewhere, or a quote in another page, for example?

If the client doesn’t want that block on that page, can they just, well… not add it? Especially if you use Structured Text, it should be easy to see the blocks as they’re added, and if they make a mistake, they can simply delete it.

That way, your client will have the ability to modify pages as they see fit (and your frontend will automatically conform) without adhering to a strict design-time schema that they cannot modify in the future. The downside is that there’s not a strict schema and they might put something in a place you didn’t anticipate :frowning:

If you’re sure you really do a different schema for each use case and validations for each instance of the Page model, then I’m afraid they might have to be separate models altogether, like you originally had it.

Or if you want a combination of separate models and reused parts within each one, you can still use separate singleton models, with some fields linking to a shared model (with embedded blocks, etc. for something like a quote that’s used in several places). But at that point I think the editor experience would get pretty complicated, with so many nested layers… IMHO probably simpler is better, with simple page layout rules shared across all the pages and easy ways to correct mistakes if they make them.


If you’re sure your client will only need those particular pages, and you don’t want to give them room to make layout mistakes, then you can do what you originally had (a single-instance model for each page) and just hardcode all of that into your frontend. e.g. the menus link to specific hard-coded slugs (or you can fetch them by a comma-separated list of IDs). And the pages would fetch individual models/pages again by their IDs. And if you ever need to add a new page, since they’re not shared anyway, you’d just change the frontend code.

I think that would probably be easier to reason about than trying to shim a reusable system on top of a bunch of separate single-instance schemas… to both your frontend and your client, it would more obvious that way that “these are the only pages you can edit, and these are the only fields you can touch” and if they need anything else done they would have to contact you to make those changes manually.

It’s a tradeoff between flexibility and strictness that you can discuss with them to see what makes the most sense (ie whether you’ll have an ongoing relationship with them after launch).

Thanks for the explanation - looks like there are a few options to play with and see what feels best. As a developer I do like just having all the content blocks available and rendered in a single template, but it can be a challenge with the design I am working with to be sure everything will fit nicely in any order. I can always leave helper text that advises on what the design team recommends as a guideline. Appreciate all the info this is really helpful!

1 Like