How to connect content data to front-end components

Once you have a component library, the next step of building a headless platform is connecting your content models to your front-end components. Your website will probably have templated and non-templated pages.

The process for building a templated page is relatively straightforward. Dynamically building a non-templated page is more complex as page content and layout will vary, depending on what the content author has selected.

Templated pages

Authors will see pre-defined content fields when composing a templated page. They won't be able to reorder components or otherwise modify the layout of the page.

Let's take a blog post as an example. Users can input the title, date, summary, and body of the blog post.

Sanity would return a JSON object that looks like this:

{
  "blogTitle": "Headless CMS essentials: Live Preview, featuring Sanity.io",
  "content": 
  "publishDate": "2023-03-22",
  "slug": "headless-live-preview",
  "summary": "By using a live preview in your headless CMS, content authors can have the best of both worlds: a familiar, visual editing experience combined with improved performance, flexibility, and scalability.",
  //... additonal fields have been omitted for brevity
}

To generate the blog post as a page, you need to map the JSON fields to the right components and map the keys from the JSON file to the appropriate place in the page template.

const BlogPost = ({ data }) => {

  const {
    blogTitle,
    content,
    publishDate,
    summary,
  } = data.page

  return (
    <StyledBlogPostPage>
      <StyledArticle>
        <StyledReadTime>
          {publishDate}
        </StyledReadTime>
        <h1>{blogTitle}</h1>
        <StyledSummary>
          <p>{summary}</p>
        </StyledSummary>
        <article>{content}</article>
      </StyledArticle>
    </StyledBlogPostPage>
  )
}

The result is a static HTML file of our blog post page.

Non-templated pages

Authors can choose from a selection of components and page modules when composing a non-templated page. They can add and move content around and change the page layout.

To build a non-templated page, we need to dynamically connect the JSON data from the CMS to our front-end components. The magic is in a single function we call buildComponent.

You'll likely see different implementations across various frameworks and starter repos. We'll walk through our general approach by explaining how we can do this in Next.js.

How to use buildComponent in Next.js

Let's look at this example JSON containing CMS content data:

Sample JSON from CMS
{
  "_key": "6288f6544a9e",
  "_type": "Quote", // Content model name should match component name
  // Content model fields should match props in components:
  "attribution": "Lance Martel, CIO, Staples Canada",
  "quotation": "Rangle gave us permission to prioritize the customer because true innovation is anchored by the customer…and how we drive better experiences for them."
}

Import all the components you need to dynamically build a given page. These should be your CMS module components from your component library.

buildComponent.jsx
import {
  Accordion,
  Footer,
  GalleryCarousel,
  HeroBanner,
  Quote,
  // ... all other components
} from '../component-library'

Map the content models in your CMS to the front-end components in your component library.

Name your content models and components so their field names and prop names match, respectively.

This is critical for matching content data from your CMS to the appropriate components in your component library, to dynamically generate a non-templated page.

If the name of a content model in your JSON doesn't match the name of its corresponding component, you'll need to explicitly define a map object to pair the names of your content model and component.

buildComponent.jsx
const componentsMap = {
  Accordion,
  Footer,
  GalleryCarousel,
  MainHero: HeroBanner, // explicitly map content models to components if their names don't match
  Quote,
  // ... all other components
}

const componentSelector = (componentType) => {
  return components[componentType]
}

Pass the component names and props from the CMS to generate the dynamic component.

buildComponent.jsx
export const buildComponent = ({_type, ...props}) => {
  if (!_type) {
    throw new Error('Object does not have a '_type' property')
  }

  const Component = componentsMap[_type]
  if (!Component) {
    throw new Error(`No component is registered for type:'${_type}`)
  }
  return <Component {...props} />
}

When we put everything together, buildComponent.jsx looks like this:

buildComponent.jsx
import {
  Accordion,
  Footer,
  GalleryCarousel,
  HeroBanner,
  Quote,
} from '../component-library'

const componentsMap = {
  Accordion,
  Footer,
  GalleryCarousel,
  HeroBanner,
  Quote,
}

const componentSelector = (componentType) => {
  return components[componentType]
}

export const buildComponent = ({_type, ...props}) => {
  if (!_type) {
    throw new Error('Object does not have a '_type' property')
  }

  const Component = componentsMap[_type]
  if (!Component) {
    throw new Error(`No component is registered for type:'${_type}`)
  }
  return <Component {...props} />
}

You can now use this function whenever you need to dynamically create components on a page template.

[...slug].jsx
import { buildComponent } from '../util/buildComponent.jsx'

const Page = ({ data }) => {
  const { modules } = data.page
  return (
    <div>
      {modules.map(module => buildComponent(module))}
    </div>
  )
}

Last updated

Rangle.io