Best way to manage internal links

I am working on a Contentful Next.js app using Compose. I have been round and round on the best way to link pages and keep track of urls.

This is the structure of the page types:

  • Company (Page Type of Landing) /company
    – Values (Page Type of Company) /company/values
  • Expertise (Page Type of Landing) /expertise
    – Industry (Page Type of Industry) /expertise/industry/[slug]

I am trying to determine the best way to link a landing page to a detail page and detail page to landing. The detail pages have a link e.g. “Back to Company” or “Back to Expertise” so it need access to the parent page title, slug and contentType. contentType is currently being used to get the url path before the slug.

  • Landing pages have content models that include a Link field that references the entry type “Compose: Page”.
  • The detail pages have a Parent Link field that reference the entry type “Compose: Page”.

This is how it ties them into a child parent relationship. I’m not sure the best way to tie them together in Contentful.

When a landing page has a content model that links to a detail page and the detail page has a reference back to the parent page, the Next.js app errors: “Reason: Circular references cannot be expressed in JSON” which makes sense because it is circular. When I change the “include” param in “getEntries” from 10 to 4 it resolves, but links do not have the “sys.contentType” anymore. Changing the include value seems like the wrong way to go.

On the detail page , should it run another query to get the full parent page link so it has access to “contentType”? Or is there a better way to structure the page types, etc.?

Thank you for any advice and help.

Hello Kelly, that’s a great question.

I see a few options here and they all come with their own set of advantages and disadvantages. Unfortunately, as all ways with content modelling, there’s not silver bullet and the solution depends on the application and creators managing the content.

Put the parent relationship into code

When I build projects including a parent/child relationship similar to your example, I usually try to avoid putting them bi-directional into the content model. It’s similar to JS dependencies and relationships, when you build up something circular all sorts of headaches can appear.

So, while it might make sense to add children links to the parent landing page to e.g. put additional text, offer more control and add imagery to the child preview I’d keep it in this one location and define (one could say “hard code”) that all /company/values pages link back to /company.

This has the advantage that you don’t have to define the parent relationship over and over again in your children entries, but has the obvious disadvantage that you have a hard coded URL somewhere in your Next.js route.

Use GraphQL

If you want to keep circular references you could also have a look at the GraphQL API. The CDA JS client does all sort of magic when resolving references. In GraphQL these shouldn’t appear, because you define the response structure.

This has the advantage that you can fetch the data you want and need, but might have a mix of REST/GQL calls in your code base. Plus, the GQL API has query complexity limits which could mean that depending on how nested your content is, you can’t fetch the same amount of content with GQL. That could lead to more needed HTTP calls.

Remove the circular dependency when passing data to Next.js

This is just a guess, but I assume that you directly pass the result of your contentful.js call to getStaticProps or the similar methods. Next.js then tries to stringify the available object and that’s what’s throwing.

You could also “manually” pick the properties to remove the circular dependency and e.g. only include the slug of the parent in the data passed to Next.

Make more HTTP calls

should it run another query to get the full parent page link so it has access to “contentType”

Yeah, that’s an option, but I don’t think that’s great either.


Let me know how it goes and I hope that’s helpful. :slight_smile: