Contentful driven page tree

Hi,

I’m currently creating the content model for a website with Compose. Previously I’ve always hardcoded the routes for the different page urls of my websites and through having a few different page types been able to fetch these by a slug field. This time my tree is a bit larger and possibly several layers deep. The data and visuals of these pages for most of these paths are basically the same and having a different content type just to make another url structure for that page work sounds silly.

I’m using Next.js for my front-end and I am now exploring a way to dynamically build up the page tree by having an optional children field with page references on my page content type. The children can have children of their own, thus creating a fully CMS driven page tree through recursion. In Next.js I’m now fetching and building up a page tree dictionary in memory by getting all these pages from top to bottom. While my proof of concept works as expected, I’m concerned for scalability and performance issues if the website grows. The problem is also that the Contentful Content API returns the content field of the page, eventually resulting in too large response sizes or linked entries limits.

I’ve been reading as much as I can about this, but there isn’t a lot to go on. I know Contentful isn’t like a traditional page-centric CMS out of the box, but I am building a website and I don’t want to create different page content types every time I want to have a nested page in a hierarchy that doesn’t yet exist. Especially since the page can stay the exact same content model wise. With the current concept of the site this will already result in 10-15 page content types already and that will most likely grow in the future.

So for a tree like this:
/a/:slug
/b/:slug
/c/:slug

Do I really need to make new content types for the pages behind a,b and c even though they might be exactly the same model-wise?

Does anyone have any ideas or tips on how to basically tackle a dynamic sitemap / page tree from within Contentful or how to prevent creating new page content types whenever it needs a different URL / path on the website (nested)?

One option would be to create a dedicated content type for defining your page tree like so (I called it Navigation in my example):

And add the unique validation to the slug field.

For the best experience, this perhaps could be paired with a small custom app that sits on the slug field and uses the links_to_entry search parameter to recursively find all the parent navigation items to then construct the complete slug for a particular navigation item. Alternatively, you could have an additional ‘complete slug’ field for which editing is disabled that is updated by a script triggered by a webhook.

You would then use these navigation entries to make your page tree like this below for example:

root(/)
  ├blog
  ├case-studies
  ├developers
  │ ├docs
  │ │ └tutorials
  │ │   └general
  │ │     └get-started
  │ └videos
  └help

Then, for example, when someone visits your site at contentful.com/developers/docs/tutorials/general/get-started/ the frontend app can make a simple API call (using the equality operator) to retrieve the navigation item with the slug (or complete slug) of /developers/docs/tutorials/general/get-started/. To make sure you don’t get too much info in the response you can use the select operator to only get the field(s) you want and effectively exclude the Sub Navigation field. Or you could use the GraphQL Content API to directly specify the fields you want at each level.

The content field could then link to various page types (e.g., landing page, blog post, help center article, etc.) but could also link to something like an entry of a redirect content type to allow you to preserve urls which might be valuable for SEO purposes.

Hope this helps!

How to programmatically create such tree structure? Any code examples?

hey @charlie do you have an example repo for this? in this example app: https://marketing-site.templates.contentful.com the /classic-card should be under products in my case. how could i achieve this?