Map modular content field to react component

Hi there. Has anyone integrated Modular Content Field inside Next.js to map a specific block to a specific React component file?

For example: if I have “profile_card” block that is used in the modular content field → I would like to map that to a specific react component file “components/profile_card.js”?

Something similar that StoryBlok offers with their Storyblok components in Next.js

I think that would be really powerful addition to DatoCMS to link blocks to certain react component. Maybe this has already been done and I don’t know about it?

Thanks,
Primoz

When you query the field, you get back a nested JSON array. You Array.map() that array into specific blocks, then have a giant switch statement on block.__typename and return a custom renderer (React snippet) for each type.

Like case block.__typename==='profile_card', return <ProfileCard name={block.fullName}/>.

<ProfileCard/> can be in its own file or not, up to you.

Next.js doesn’t really come much into it except that you can prefetch/prerender the query using getStaticProps if you want to. Other than that it’s mostly just standard JSON and React.

The DatoCMS react project has examples for rendering custom blocks inside Structured Text Fields. They’re much the same inside Modular Content Fields.

Hopefully this gets you started:

AFAIK there isn’t a very out-of-the-box DatoCMS integration, so to Next.js, DatoCMS is mostly just a way to get you a JSON blob of data. The rest is mostly up to you, except the occasional first-party React component that Dato provides (like <StructuredText/>, but there isn’t one for Modular Content Fields (AFAIK).

My guess is that MCF was an older tech that eventually gave way to Structured Text, but kept in there for backward compatibility reasons? Not sure. Regardless, even if you used Structured Text instead of MCF, you’d still have to write custom renderers for every block.

1 Like

As a more complete example, on our Next.js site, the page template uses this map:
myPageData.modularContentField.map((block, key) => <BlockRenderer key={key} block={block}/>)

And <BlockRenderer/> is imported into that page template. The component itself looks something like this (snipped):

export function BlockRenderer(props) {
  const block = props.block;
  const type = block.__typename;

  switch (type) {
    case 'PageMessageRecord':
      return <StandardRow><div className={"basic-page__message basic-page__message--line"}><p>{block.pageMessage}</p></div></StandardRow>;

    case 'HeaderRecord':
      const HeadingTag = `h${block.level}`;
      return <StandardRow>
        <HeadingTag id={block.anchor}>{block.text}</HeadingTag>
      </StandardRow>;

    case 'ParagraphRecord':
      return <StandardRow><StructuredText data={block.body}/></StandardRow>

  
    case 'PullQuoteRecord':
      return <div className="container mb-5 mb-md-6">
        <div className="row">
          <div className="col-md-8 offset-md-2 col-12">
            <blockquote className="pull-quote pull-quote--blue">
              <p>{block.quote1}</p>
              <cite className="h6 mb-0">
                {block.source1}
              </cite>
            </blockquote>
          </div>
        </div>
      </div>

    case 'SingleImageRecord':
      return <StandardRow>
        <Image data={block.image.responsiveImage}/>
      </StandardRow>


    case'TextAreaRecord':
      return <StandardRow>
        <StructuredText data={block.content} />
      </StandardRow>

    default:
      return <StandardRow>
        <details className={"my-2 alert alert-danger"}>
          <summary>Eeps! Unhandled block: {block.__typename}</summary>
          <pre>{JSON.stringify(block, null, 2)}</pre>
        </details>
      </StandardRow>;
  }

As you can see, some of those components are rendered inline while others use their own components. But all it is, is one big switch statement. Our actual file is much longer (and messier, sadly).

Edit: If you’re looking for some sort of automatic routing between a block.__typename and an equivalent filename (kinda like how Next.js routes for pages based on filename), I don’t believe that exists out of the box (feature request?). But it should be possible to do that just by looking for a file of the same name and loading it… if not in pure JS, then maybe using webpack/babel rules or such? But essentially you end up writing your own filesystem-based routing code in lieu of a switch statement.

2 Likes

Cool thanks for your input, this helps.

I do believe if something like this was a part of official react-datocms package this would be a great benefit. Maybe have a wrapper component inside this package that would handle mapping of content blocks to your component.js files, similar to structured text component.

I see structured text field useful for editorial content like blog posts, where modular content is perfect for landing pages, where you can compose them with the blocks you have defined.

What is your opinion on this guys?

2 Likes

I see structured text field useful for editorial content like blog posts, where modular content is perfect for landing pages, where you can compose them with the blocks you have defined.

Correct! More about block records here: Modular blocks documentation — DatoCMS

I do believe if something like this was a part of official react-datocms package this would be a great benefit. Maybe have a wrapper component inside this package that would handle mapping of content blocks to your component.js files, similar to structured text component.

I’ll convert this topic to a feature request!

2 Likes

Remember to upvote this if you are interested :wink:

1 Like

@primoz.rome As we built out more and more components, our switch statement got horrendously big. I refactored the component code out into a separate file using Next.js dynamic imports, while keeping only the barebones loader logic in the switch statement.

Block renderer:

import dynamic from 'next/dynamic';

switch(block.__typename) {
      case 'ImageGalleryRecord':
        const ImageGallery = dynamic(() => import('./blocks/ImageGalleryRecord'))
        return <ImageGallery block={block}/>

      case 'SomeOtherRecord':
        return <div>You can also mix and match, using inline elements for simpler components and dynamic imports for the longer ones.</div>
}

And then in ./blocks/ImageGalleryRecord.js

export default function ImageGalleryRecord(props) {
return <>
  My actual component goes here...
</>
}
2 Likes

@roger that seems like a fair solution…

I am not sure though what does the block prop does that you push down to ImageGallery component? It is not defined in your pasted code…

@primoz.rome My apologies. The block is just the GraphQL API response for the particular block inside the Modular Content array field. The entire field is an array (see example field below), and each block is a single element of that array, like:

{props.page.modularContentField.map((block, key) => <BlockRenderer key={key} block={block}/>)}

So the whole thing would fit together like:

  1. Query DatoCMS inside getStaticProps() and return the API response
  2. The page function (default function MyPage(props)) receives the response as something like props.page (where page is the model name in Dato)
  3. Then you must props.page.modularContentField.map the MCF field array into specific blocks.
  4. For each block, use Next.js to dynamically import a file and return its component, passing along the block to the component so it can handle the actual content (in this case, the image parameters)
  5. That’s it! (~Future Todo: Figure out a way to auto-import a component based on its name & filename, instead of having to still use a switch statement for each .__typename.~ Unfortunately this doesn’t seem possible in Next.js)

Here’s an example Modular Content field. The block would just be a single element of this array. Sorry for the length of this! You can change what fields you get back in the MCF by modifying the GraphQL query. In our case, we re-use an GraphQL fragment for our images across the site, which is why this has so many fields. In any case, here it is:

[
  {
    "__typename": "HeaderRecord",
    "id": "54208740",
    "text": "Image Gallery - Slide",
    "level": "2",
    "anchor": "image-galleries-under-development"
  },
  {
    "__typename": "ImageGalleryRecord",
    "id": "54374542",
    "images": [
      {
        "alt": "A woven basket that’s red and covered in spikes next to its lid, which has green leaves and stem and a white flower.",
        "basename": "black-ash-basketexhibitions022021item-3589",
        "blurhash": "LC9r%@[ErY={aKxGs.Na0}OrkWE2",
        "blurUpThumb": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQFQ0NDh0VHR0dJSclJBYfHSYmIS0vGh0tHR0WMDUxKC0vMjIyHSI4PTcwPCsxMjUBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7OzsvOzs7Ozs7Oy87Oy87Ozs7NTs7Oy8vNTs7OzsvLy8vOy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAZAAABBQAAAAAAAAAAAAAAAAAFAAEDBAf/xAAdEAABBQEBAQEAAAAAAAAAAAABAAIDBREEIVEU/8QAFgEBAQEAAAAAAAAAAAAAAAAABQMC/8QAHBEBAAIBBQAAAAAAAAAAAAAAAQACAwQRISJB/9oADAMBAAIRAxEAPwDO+yuPON3Qm5aJvY0OdulTWFkyduAq1V2McUYGqXcOYvq7Y0D2CbGg/JGXNcfPqSJXNkyWItb6Skt1LJCbWxjtP//Z",
        "exifInfo": {},
        "customData": {},
        "copyright": "Michelle Kuo",
        "filename": "black-ash-basketexhibitions022021item-3589.jpg",
        "focalPoint": {
          "x": 0.5,
          "y": 0.5
        },
        "height": 933,
        "id": "19320646",
        "mimeType": "image/jpeg",
        "size": 129905,
        "smartTags": [
          "paper",
          "origami",
          "art"
        ],
        "tags": [],
        "title": "Strawberries and blueberries are sacred fruits to the Pokagon Potawatomi people. This strawberry basket by Jamie Chapman is covered in curled spikes called curlicues, which require time and masterful skill to weave.",
        "url": "https://www.datocms-assets.com/44232/1625853067-black-ash-basketexhibitions022021item-3589.jpg",
        "width": 1400,
        "responsiveImage": {
          "srcSet": "https://www.datocms-assets.com/44232/1625853067-black-ash-basketexhibitions022021item-3589.jpg?dpr=0.25 350w,https://www.datocms-assets.com/44232/1625853067-black-ash-basketexhibitions022021item-3589.jpg?dpr=0.5 700w,https://www.datocms-assets.com/44232/1625853067-black-ash-basketexhibitions022021item-3589.jpg?dpr=0.75 1050w,https://www.datocms-assets.com/44232/1625853067-black-ash-basketexhibitions022021item-3589.jpg 1400w",
          "webpSrcSet": "https://www.datocms-assets.com/44232/1625853067-black-ash-basketexhibitions022021item-3589.jpg?dpr=0.25&fm=webp 350w,https://www.datocms-assets.com/44232/1625853067-black-ash-basketexhibitions022021item-3589.jpg?dpr=0.5&fm=webp 700w,https://www.datocms-assets.com/44232/1625853067-black-ash-basketexhibitions022021item-3589.jpg?dpr=0.75&fm=webp 1050w,https://www.datocms-assets.com/44232/1625853067-black-ash-basketexhibitions022021item-3589.jpg?fm=webp 1400w",
          "sizes": "(max-width: 1400px) 100vw, 1400px",
          "src": "https://www.datocms-assets.com/44232/1625853067-black-ash-basketexhibitions022021item-3589.jpg",
          "width": 1400,
          "height": 933,
          "aspectRatio": 1.5005359056806002,
          "alt": "A woven basket that’s red and covered in spikes next to its lid, which has green leaves and stem and a white flower.",
          "title": "Strawberries and blueberries are sacred fruits to the Pokagon Potawatomi people. This strawberry basket by Jamie Chapman is covered in curled spikes called curlicues, which require time and masterful skill to weave.",
          "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQFQ0NDh0VHR0dJSclJBYfHSYmIS0vGh0tHR0WMDUxKC0vMjIyHSI4PTcwPCsxMjUBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7OzsvOzs7Ozs7Oy87Oy87Ozs7NTs7Oy8vNTs7OzsvLy8vOy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAZAAABBQAAAAAAAAAAAAAAAAAFAAEDBAf/xAAdEAABBQEBAQEAAAAAAAAAAAABAAIDBREEIVEU/8QAFgEBAQEAAAAAAAAAAAAAAAAABQMC/8QAHBEBAAIBBQAAAAAAAAAAAAAAAQACAwQRISJB/9oADAMBAAIRAxEAPwDO+yuPON3Qm5aJvY0OdulTWFkyduAq1V2McUYGqXcOYvq7Y0D2CbGg/JGXNcfPqSJXNkyWItb6Skt1LJCbWxjtP//Z"
        }
      },
      {
        "alt": "A young boy wearing a face mask poses for a photo next to a dinosaur bone that's twice his height, in front of a mural of a rocky landscape. Another young boy and a man take his photo.",
        "basename": "michelle-kuo-field-museummktg071720196d-1",
        "blurhash": "LOG[sJrs?b-=~pIB%MoztRxvRkRi",
        "blurUpThumb": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLEg0PDiMQDQ0NFxgMFhYOFxoZGCIfFhUaHysjGh0oHRUWJDUlKC0vMjIyGSU4PTcwPCsxMi8BCgsLDg0OHBAQHC8oIhw7Oy87Oy8vLy8vLzsvLzUvOzUvOy8vLy8vLy8vLy8vLzUvLy8vLy8vLy8vLy8vLy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAZAAACAwEAAAAAAAAAAAAAAAAFBgEDBwD/xAAeEAABBAIDAQAAAAAAAAAAAAABAAIDBQRBESExEv/EABUBAQEAAAAAAAAAAAAAAAAAAAMB/8QAGREAAgMBAAAAAAAAAAAAAAAAACEBERIC/9oADAMBAAIRAxEAPwBht85jIiSUGrrFks3R2q7dzpYSOUBwfvGlJ52jqdCqjQ2ZbAwdqUmzXJjZ6uVnlhn/2Q==",
        "exifInfo": {},
        "customData": {},
        "copyright": "Photo by Michelle Kuo, © 2012 The Field Museum,",
        "filename": "michelle-kuo-field-museummktg071720196d-1.jpg",
        "focalPoint": {
          "x": 0.37,
          "y": 0.52
        },
        "height": 933,
        "id": "19421930",
        "mimeType": "image/jpeg",
        "size": 246951,
        "smartTags": [
          "shorts",
          "clothing",
          "apparel",
          "person",
          "human",
          "shoe",
          "footwear",
          "helmet",
          "soil"
        ],
        "tags": [],
        "title": null,
        "url": "https://www.datocms-assets.com/44232/1626123758-michelle-kuo-field-museummktg071720196d-1.jpg",
        "width": 1400,
        "responsiveImage": {
          "srcSet": "https://www.datocms-assets.com/44232/1626123758-michelle-kuo-field-museummktg071720196d-1.jpg?dpr=0.25 350w,https://www.datocms-assets.com/44232/1626123758-michelle-kuo-field-museummktg071720196d-1.jpg?dpr=0.5 700w,https://www.datocms-assets.com/44232/1626123758-michelle-kuo-field-museummktg071720196d-1.jpg?dpr=0.75 1050w,https://www.datocms-assets.com/44232/1626123758-michelle-kuo-field-museummktg071720196d-1.jpg 1400w",
          "webpSrcSet": "https://www.datocms-assets.com/44232/1626123758-michelle-kuo-field-museummktg071720196d-1.jpg?dpr=0.25&fm=webp 350w,https://www.datocms-assets.com/44232/1626123758-michelle-kuo-field-museummktg071720196d-1.jpg?dpr=0.5&fm=webp 700w,https://www.datocms-assets.com/44232/1626123758-michelle-kuo-field-museummktg071720196d-1.jpg?dpr=0.75&fm=webp 1050w,https://www.datocms-assets.com/44232/1626123758-michelle-kuo-field-museummktg071720196d-1.jpg?fm=webp 1400w",
          "sizes": "(max-width: 1400px) 100vw, 1400px",
          "src": "https://www.datocms-assets.com/44232/1626123758-michelle-kuo-field-museummktg071720196d-1.jpg",
          "width": 1400,
          "height": 933,
          "aspectRatio": 1.5005359056806002,
          "alt": "A young boy wearing a face mask poses for a photo next to a dinosaur bone that's twice his height, in front of a mural of a rocky landscape. Another young boy and a man take his photo.",
          "title": null,
          "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLEg0PDiMQDQ0NFxgMFhYOFxoZGCIfFhUaHysjGh0oHRUWJDUlKC0vMjIyGSU4PTcwPCsxMi8BCgsLDg0OHBAQHC8oIhw7Oy87Oy8vLy8vLzsvLzUvOzUvOy8vLy8vLy8vLy8vLzUvLy8vLy8vLy8vLy8vLy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAZAAACAwEAAAAAAAAAAAAAAAAFBgEDBwD/xAAeEAABBAIDAQAAAAAAAAAAAAABAAIDBQRBESExEv/EABUBAQEAAAAAAAAAAAAAAAAAAAMB/8QAGREAAgMBAAAAAAAAAAAAAAAAACEBERIC/9oADAMBAAIRAxEAPwBht85jIiSUGrrFks3R2q7dzpYSOUBwfvGlJ52jqdCqjQ2ZbAwdqUmzXJjZ6uVnlhn/2Q=="
        }
      },
      {
        "alt": "Four women dressed in Apsáalooke attire stand in front of the Field Museum. ",
        "basename": "c-adam-sings-in-the-timber-20190419-cam22610",
        "blurhash": "LFG*jBM_.mbv0hkDIpogKiX9m-r@",
        "blurUpThumb": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoIEggQChALDhUXDQ0NFhEODQ0OFxMZGBYVFhUaHysjGh0oHRUWJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHBAQHC8cIh07Ly87Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAYAAACAwAAAAAAAAAAAAAAAAAABQMEBv/EABwQAAIDAAMBAAAAAAAAAAAAAAECAAMEESEjEv/EABYBAQEBAAAAAAAAAAAAAAAAAAMEAv/EABoRAAICAwAAAAAAAAAAAAAAAAECAAMREiH/2gAMAwEAAhEDEQA/AI9dJVIrrf1+ZptdIZD1E4xgXc8QzYcyupK9ey1noLqIRlipAUQmw8BkGZ//2Q==",
        "exifInfo": {},
        "customData": {},
        "copyright": " Adam Sings In The Timber",
        "filename": "c-adam-sings-in-the-timber-20190419-cam22610.jpg",
        "focalPoint": {
          "x": 0.41,
          "y": 0.12
        },
        "height": 933,
        "id": "22486932",
        "mimeType": "image/jpeg",
        "size": 364961,
        "smartTags": [
          "costume",
          "person",
          "human",
          "festival",
          "crowd",
          "clothing",
          "apparel"
        ],
        "tags": [],
        "title": "From left to right: Phenocia Bauerle, Charmaine Hill, Nina Sanders, JoRee LaFrance.",
        "url": "https://www.datocms-assets.com/44232/1629989908-c-adam-sings-in-the-timber-20190419-cam22610.jpg",
        "width": 1400,
        "responsiveImage": {
          "srcSet": "https://www.datocms-assets.com/44232/1629989908-c-adam-sings-in-the-timber-20190419-cam22610.jpg?dpr=0.25 350w,https://www.datocms-assets.com/44232/1629989908-c-adam-sings-in-the-timber-20190419-cam22610.jpg?dpr=0.5 700w,https://www.datocms-assets.com/44232/1629989908-c-adam-sings-in-the-timber-20190419-cam22610.jpg?dpr=0.75 1050w,https://www.datocms-assets.com/44232/1629989908-c-adam-sings-in-the-timber-20190419-cam22610.jpg 1400w",
          "webpSrcSet": "https://www.datocms-assets.com/44232/1629989908-c-adam-sings-in-the-timber-20190419-cam22610.jpg?dpr=0.25&fm=webp 350w,https://www.datocms-assets.com/44232/1629989908-c-adam-sings-in-the-timber-20190419-cam22610.jpg?dpr=0.5&fm=webp 700w,https://www.datocms-assets.com/44232/1629989908-c-adam-sings-in-the-timber-20190419-cam22610.jpg?dpr=0.75&fm=webp 1050w,https://www.datocms-assets.com/44232/1629989908-c-adam-sings-in-the-timber-20190419-cam22610.jpg?fm=webp 1400w",
          "sizes": "(max-width: 1400px) 100vw, 1400px",
          "src": "https://www.datocms-assets.com/44232/1629989908-c-adam-sings-in-the-timber-20190419-cam22610.jpg",
          "width": 1400,
          "height": 933,
          "aspectRatio": 1.5005359056806002,
          "alt": "Four women dressed in Apsáalooke attire stand in front of the Field Museum. ",
          "title": "From left to right: Phenocia Bauerle, Charmaine Hill, Nina Sanders, JoRee LaFrance.",
          "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoIEggQChALDhUXDQ0NFhEODQ0OFxMZGBYVFhUaHysjGh0oHRUWJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHBAQHC8cIh07Ly87Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAYAAACAwAAAAAAAAAAAAAAAAAABQMEBv/EABwQAAIDAAMBAAAAAAAAAAAAAAECAAMEESEjEv/EABYBAQEBAAAAAAAAAAAAAAAAAAMEAv/EABoRAAICAwAAAAAAAAAAAAAAAAECAAMREiH/2gAMAwEAAhEDEQA/AI9dJVIrrf1+ZptdIZD1E4xgXc8QzYcyupK9ey1noLqIRlipAUQmw8BkGZ//2Q=="
        }
      },
      {
        "alt": "This is a test image",
        "basename": "test-color-pattern",
        "blurhash": "LFI#u+.4p1+7TfS~bnVc~pVzIqTu",
        "blurUpThumb": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHCw4RCg4TEAoPDg4ODQkPDxMNFg0YFxUZGBYVFiEaHzcjGh0oHRUWJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHBAQHC8oIiY1Ly87Oy8vLy8vLy8vLzUvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABIAGAMBIgACEQEDEQH/xAAaAAACAgMAAAAAAAAAAAAAAAAABAEDAgUG/8QAGhAAAwEAAwAAAAAAAAAAAAAAAAIDAQQFFP/EABcBAAMBAAAAAAAAAAAAAAAAAAIDBAD/xAAdEQACAgEFAAAAAAAAAAAAAAAAAQIxIQMREyJh/9oADAMBAAIRAxEAPwDqPWotfsEQwaDZgjfhUqA5JSyZRfJ4Op2CuAhLhPIgdsnQGqu2Df1KlzCQI9WyhFdswAAqhQt2f//Z",
        "exifInfo": {},
        "customData": {},
        "copyright": null,
        "filename": "test-color-pattern.jpg",
        "focalPoint": {
          "x": 0.51,
          "y": 0.77
        },
        "height": 993,
        "id": "17437392",
        "mimeType": "image/jpeg",
        "size": 120001,
        "smartTags": [
          "text",
          "label"
        ],
        "tags": [],
        "title": null,
        "url": "https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg",
        "width": 1300,
        "responsiveImage": {
          "srcSet": "https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?dpr=0.25 325w,https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?dpr=0.5 650w,https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?dpr=0.75 975w,https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg 1300w",
          "webpSrcSet": "https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?dpr=0.25&fm=webp 325w,https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?dpr=0.5&fm=webp 650w,https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?dpr=0.75&fm=webp 975w,https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?fm=webp 1300w",
          "sizes": "(max-width: 1300px) 100vw, 1300px",
          "src": "https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg",
          "width": 1300,
          "height": 993,
          "aspectRatio": 1.309164149043303,
          "alt": "This is a test image",
          "title": null,
          "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHCw4RCg4TEAoPDg4ODQkPDxMNFg0YFxUZGBYVFiEaHzcjGh0oHRUWJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHBAQHC8oIiY1Ly87Oy8vLy8vLy8vLzUvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABIAGAMBIgACEQEDEQH/xAAaAAACAgMAAAAAAAAAAAAAAAAABAEDAgUG/8QAGhAAAwEAAwAAAAAAAAAAAAAAAAIDAQQFFP/EABcBAAMBAAAAAAAAAAAAAAAAAAIDBAD/xAAdEQACAgEFAAAAAAAAAAAAAAAAAQIxIQMREyJh/9oADAMBAAIRAxEAPwDqPWotfsEQwaDZgjfhUqA5JSyZRfJ4Op2CuAhLhPIgdsnQGqu2Df1KlzCQI9WyhFdswAAqhQt2f//Z"
        }
      },
      {
        "alt": "The accessible East entrance is on the side of the building that's closest to Lake Michigan and farthest from Lake Shore Drive. ",
        "basename": "field-museum-east-entrance-google-maps-1",
        "blurhash": "LFE.,~~WD%sk9a-:xuIVs+M|ogxu",
        "blurUpThumb": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoIEggLEA8VDg4QDQ0LDhEOFg0SFxQZGBYTFhUmHysjJh0oHSEWJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHBAQHC8oHSgvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAZAAACAwEAAAAAAAAAAAAAAAADBAEFBgD/xAAdEAACAQQDAAAAAAAAAAAAAAAAAQIDBBESBSEi/8QAFgEBAQEAAAAAAAAAAAAAAAAAAwIA/8QAGBEBAQADAAAAAAAAAAAAAAAAAQACEUH/2gAMAwEAAhEDEQA/ADxWwre0moMbXgI6arxD5Wlir2Mt30QXnJWEY5eDjGRG4u7/2Q==",
        "exifInfo": {},
        "customData": {},
        "copyright": "Google",
        "filename": "field-museum-east-entrance-google-maps-1.jpg",
        "focalPoint": {
          "x": 0.43,
          "y": 0.73
        },
        "height": 933,
        "id": "16573481",
        "mimeType": "image/jpeg",
        "size": 276555,
        "smartTags": [
          "person",
          "human",
          "pedestrian"
        ],
        "tags": [],
        "title": "Field Museum East Entrance",
        "url": "https://www.datocms-assets.com/44232/1621982756-field-museum-east-entrance-google-maps-1.jpg",
        "width": 1400,
        "responsiveImage": {
          "srcSet": "https://www.datocms-assets.com/44232/1621982756-field-museum-east-entrance-google-maps-1.jpg?dpr=0.25 350w,https://www.datocms-assets.com/44232/1621982756-field-museum-east-entrance-google-maps-1.jpg?dpr=0.5 700w,https://www.datocms-assets.com/44232/1621982756-field-museum-east-entrance-google-maps-1.jpg?dpr=0.75 1050w,https://www.datocms-assets.com/44232/1621982756-field-museum-east-entrance-google-maps-1.jpg 1400w",
          "webpSrcSet": "https://www.datocms-assets.com/44232/1621982756-field-museum-east-entrance-google-maps-1.jpg?dpr=0.25&fm=webp 350w,https://www.datocms-assets.com/44232/1621982756-field-museum-east-entrance-google-maps-1.jpg?dpr=0.5&fm=webp 700w,https://www.datocms-assets.com/44232/1621982756-field-museum-east-entrance-google-maps-1.jpg?dpr=0.75&fm=webp 1050w,https://www.datocms-assets.com/44232/1621982756-field-museum-east-entrance-google-maps-1.jpg?fm=webp 1400w",
          "sizes": "(max-width: 1400px) 100vw, 1400px",
          "src": "https://www.datocms-assets.com/44232/1621982756-field-museum-east-entrance-google-maps-1.jpg",
          "width": 1400,
          "height": 933,
          "aspectRatio": 1.5005359056806002,
          "alt": "The accessible East entrance is on the side of the building that's closest to Lake Michigan and farthest from Lake Shore Drive. ",
          "title": "Field Museum East Entrance",
          "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoIEggLEA8VDg4QDQ0LDhEOFg0SFxQZGBYTFhUmHysjJh0oHSEWJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHBAQHC8oHSgvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAZAAACAwEAAAAAAAAAAAAAAAADBAEFBgD/xAAdEAACAQQDAAAAAAAAAAAAAAAAAQIDBBESBSEi/8QAFgEBAQEAAAAAAAAAAAAAAAAAAwIA/8QAGBEBAQADAAAAAAAAAAAAAAAAAQACEUH/2gAMAwEAAhEDEQA/ADxWwre0moMbXgI6arxD5Wlir2Mt30QXnJWEY5eDjGRG4u7/2Q=="
        }
      },
      {
        "alt": "This is a test image",
        "basename": "test-color-pattern",
        "blurhash": "LFI#u+.4p1+7TfS~bnVc~pVzIqTu",
        "blurUpThumb": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHCw4RCg4TEAoPDg4ODQkPDxMNFg0YFxUZGBYVFiEaHzcjGh0oHRUWJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHBAQHC8oIiY1Ly87Oy8vLy8vLy8vLzUvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABIAGAMBIgACEQEDEQH/xAAaAAACAgMAAAAAAAAAAAAAAAAABAEDAgUG/8QAGhAAAwEAAwAAAAAAAAAAAAAAAAIDAQQFFP/EABcBAAMBAAAAAAAAAAAAAAAAAAIDBAD/xAAdEQACAgEFAAAAAAAAAAAAAAAAAQIxIQMREyJh/9oADAMBAAIRAxEAPwDqPWotfsEQwaDZgjfhUqA5JSyZRfJ4Op2CuAhLhPIgdsnQGqu2Df1KlzCQI9WyhFdswAAqhQt2f//Z",
        "exifInfo": {},
        "customData": {},
        "copyright": null,
        "filename": "test-color-pattern.jpg",
        "focalPoint": {
          "x": 0.51,
          "y": 0.77
        },
        "height": 993,
        "id": "17437392",
        "mimeType": "image/jpeg",
        "size": 120001,
        "smartTags": [
          "text",
          "label"
        ],
        "tags": [],
        "title": null,
        "url": "https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg",
        "width": 1300,
        "responsiveImage": {
          "srcSet": "https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?dpr=0.25 325w,https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?dpr=0.5 650w,https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?dpr=0.75 975w,https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg 1300w",
          "webpSrcSet": "https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?dpr=0.25&fm=webp 325w,https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?dpr=0.5&fm=webp 650w,https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?dpr=0.75&fm=webp 975w,https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg?fm=webp 1300w",
          "sizes": "(max-width: 1300px) 100vw, 1300px",
          "src": "https://www.datocms-assets.com/44232/1623445368-test-color-pattern.jpg",
          "width": 1300,
          "height": 993,
          "aspectRatio": 1.309164149043303,
          "alt": "This is a test image",
          "title": null,
          "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHCw4RCg4TEAoPDg4ODQkPDxMNFg0YFxUZGBYVFiEaHzcjGh0oHRUWJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHBAQHC8oIiY1Ly87Oy8vLy8vLy8vLzUvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABIAGAMBIgACEQEDEQH/xAAaAAACAgMAAAAAAAAAAAAAAAAABAEDAgUG/8QAGhAAAwEAAwAAAAAAAAAAAAAAAAIDAQQFFP/EABcBAAMBAAAAAAAAAAAAAAAAAAIDBAD/xAAdEQACAgEFAAAAAAAAAAAAAAAAAQIxIQMREyJh/9oADAMBAAIRAxEAPwDqPWotfsEQwaDZgjfhUqA5JSyZRfJ4Op2CuAhLhPIgdsnQGqu2Df1KlzCQI9WyhFdswAAqhQt2f//Z"
        }
      },
      {
        "alt": "Black ash basket",
        "basename": "black-ash-basketexhibitions022021item-2833",
        "blurhash": "LADRD}t75SI;wvI;$%s.0~Eg%0xZ",
        "blurUpThumb": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoIEAgNDRAKDg0NCA0ODhEaFg4YFxUZGBYVIhUaHysjGh0oHRUiJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHRAQHDsdIhwvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAXAAADAQAAAAAAAAAAAAAAAAAAAwQG/8QAHRAAAQUAAwEAAAAAAAAAAAAAAAECAwQREhMiBf/EABUBAQEAAAAAAAAAAAAAAAAAAAUB/8QAGREAAgMBAAAAAAAAAAAAAAAAABIBAhEh/9oADAMBAAIRAxEAPwDKQ0uxSlfn8G6Jp3GopbLcarA60W0TqmcIXx4uAJns+gIslaD/2Q==",
        "exifInfo": {},
        "customData": {},
        "copyright": null,
        "filename": "black-ash-basketexhibitions022021item-2833.jpg",
        "focalPoint": {
          "x": 0.56,
          "y": 0.52
        },
        "height": 933,
        "id": "19320136",
        "mimeType": "image/jpeg",
        "size": 167285,
        "smartTags": [
          "basket",
          "purse",
          "accessories",
          "bag",
          "handbag",
          "accessory"
        ],
        "tags": [],
        "title": null,
        "url": "https://www.datocms-assets.com/44232/1625852224-black-ash-basketexhibitions022021item-2833.jpg",
        "width": 1400,
        "responsiveImage": {
          "srcSet": "https://www.datocms-assets.com/44232/1625852224-black-ash-basketexhibitions022021item-2833.jpg?dpr=0.25 350w,https://www.datocms-assets.com/44232/1625852224-black-ash-basketexhibitions022021item-2833.jpg?dpr=0.5 700w,https://www.datocms-assets.com/44232/1625852224-black-ash-basketexhibitions022021item-2833.jpg?dpr=0.75 1050w,https://www.datocms-assets.com/44232/1625852224-black-ash-basketexhibitions022021item-2833.jpg 1400w",
          "webpSrcSet": "https://www.datocms-assets.com/44232/1625852224-black-ash-basketexhibitions022021item-2833.jpg?dpr=0.25&fm=webp 350w,https://www.datocms-assets.com/44232/1625852224-black-ash-basketexhibitions022021item-2833.jpg?dpr=0.5&fm=webp 700w,https://www.datocms-assets.com/44232/1625852224-black-ash-basketexhibitions022021item-2833.jpg?dpr=0.75&fm=webp 1050w,https://www.datocms-assets.com/44232/1625852224-black-ash-basketexhibitions022021item-2833.jpg?fm=webp 1400w",
          "sizes": "(max-width: 1400px) 100vw, 1400px",
          "src": "https://www.datocms-assets.com/44232/1625852224-black-ash-basketexhibitions022021item-2833.jpg",
          "width": 1400,
          "height": 933,
          "aspectRatio": 1.5005359056806002,
          "alt": "Black ash basket",
          "title": null,
          "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoIEAgNDRAKDg0NCA0ODhEaFg4YFxUZGBYVIhUaHysjGh0oHRUiJDUlKC0vMjIyGSI4PTcwPCsxMi8BCgsLDg0OHRAQHDsdIhwvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL//AABEIABAAGAMBIgACEQEDEQH/xAAXAAADAQAAAAAAAAAAAAAAAAAAAwQG/8QAHRAAAQUAAwEAAAAAAAAAAAAAAAECAwQREhMiBf/EABUBAQEAAAAAAAAAAAAAAAAAAAUB/8QAGREAAgMBAAAAAAAAAAAAAAAAABIBAhEh/9oADAMBAAIRAxEAPwDKQ0uxSlfn8G6Jp3GopbLcarA60W0TqmcIXx4uAJns+gIslaD/2Q=="
        }
      },
      {
        "alt": "A woven tan and red basket. ",
        "basename": "pokagon-baskets-secondary-image-cutout",
        "blurhash": "L-OWB8xG_No#xafQayj??vW;RPs:",
        "blurUpThumb": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0gFhEYIx8lJCIfIiEtKysvKCk0NSEeJEExNDk7PjU+LSJEPkM6SCsxPi8BCgsLDg0OHBAQHDsoIig7Oy87Ozs7Ozs7Ozs7OzsvOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzU7Oy87Ozs7Lzs7O//AABEIABAAGAMBIgACEQEDEQH/xAAXAAEAAwAAAAAAAAAAAAAAAAAHAAUG/8QAIhAAAQIFBAMAAAAAAAAAAAAAAQIEAAMFESEGEhMxBxRB/8QAFQEBAQAAAAAAAAAAAAAAAAAABAP/xAAbEQACAgMBAAAAAAAAAAAAAAABEgAhAxExAv/aAAwDAQACEQMRAD8AUqtVpLFqpRPyDhXkthKqHGVG26262I3tfo4ds1pBwRaCKb4zXOqV+Y8e7q2Yh7ZqjsAwIX7F6j6ikP26VJIIIwREiv07pz0GqJaekiwiRW4Ikbqf/9k=",
        "exifInfo": {},
        "customData": {},
        "copyright": "MORGAN ANDERSON",
        "filename": "pokagon-baskets-secondary-image-cutout.png",
        "focalPoint": {
          "x": 0.49,
          "y": 0.52
        },
        "height": 1333,
        "id": "19320627",
        "mimeType": "image/png",
        "size": 3236106,
        "smartTags": [
          "basket"
        ],
        "tags": [],
        "title": null,
        "url": "https://www.datocms-assets.com/44232/1629212638-pokagon-baskets-secondary-image-cutout.png",
        "width": 2000,
        "responsiveImage": {
          "srcSet": "https://www.datocms-assets.com/44232/1629212638-pokagon-baskets-secondary-image-cutout.png?dpr=0.25 500w,https://www.datocms-assets.com/44232/1629212638-pokagon-baskets-secondary-image-cutout.png?dpr=0.5 1000w,https://www.datocms-assets.com/44232/1629212638-pokagon-baskets-secondary-image-cutout.png?dpr=0.75 1500w,https://www.datocms-assets.com/44232/1629212638-pokagon-baskets-secondary-image-cutout.png 2000w",
          "webpSrcSet": "https://www.datocms-assets.com/44232/1629212638-pokagon-baskets-secondary-image-cutout.png?dpr=0.25&fm=webp 500w,https://www.datocms-assets.com/44232/1629212638-pokagon-baskets-secondary-image-cutout.png?dpr=0.5&fm=webp 1000w,https://www.datocms-assets.com/44232/1629212638-pokagon-baskets-secondary-image-cutout.png?dpr=0.75&fm=webp 1500w,https://www.datocms-assets.com/44232/1629212638-pokagon-baskets-secondary-image-cutout.png?fm=webp 2000w",
          "sizes": "(max-width: 2000px) 100vw, 2000px",
          "src": "https://www.datocms-assets.com/44232/1629212638-pokagon-baskets-secondary-image-cutout.png",
          "width": 2000,
          "height": 1333,
          "aspectRatio": 1.5003750937734435,
          "alt": "A woven tan and red basket. ",
          "title": null,
          "base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0gFhEYIx8lJCIfIiEtKysvKCk0NSEeJEExNDk7PjU+LSJEPkM6SCsxPi8BCgsLDg0OHBAQHDsoIig7Oy87Ozs7Ozs7Ozs7OzsvOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzU7Oy87Ozs7Lzs7O//AABEIABAAGAMBIgACEQEDEQH/xAAXAAEAAwAAAAAAAAAAAAAAAAAHAAUG/8QAIhAAAQIFBAMAAAAAAAAAAAAAAQIEAAMFESEGEhMxBxRB/8QAFQEBAQAAAAAAAAAAAAAAAAAABAP/xAAbEQACAgMBAAAAAAAAAAAAAAABEgAhAxExAv/aAAwDAQACEQMRAD8AUqtVpLFqpRPyDhXkthKqHGVG26262I3tfo4ds1pBwRaCKb4zXOqV+Y8e7q2Yh7ZqjsAwIX7F6j6ikP26VJIIIwREiv07pz0GqJaekiwiRW4Ikbqf/9k="
        }
      }
    ],
    "galleryStyle": "Horizontal Slides"
  },
  {
    "__typename": "HeaderRecord",
    "id": "54382924",
    "text": "Image Gallery - Scroll",
    "level": "2",
    "anchor": "image-gallery-scroll"
  }
]

And when you map.() that, each block would be a single object (i.e. a single image or header or whatever kinda MCF block, with all its data) inside that array.

Hope that makes it clearer…

2 Likes

@roger you are awesome

1 Like

Thanks, mat_jack1! I hope to open-source as much of our site template as possible, once it’s done.

2 Likes

Excellent, thank your @roger.

Hi @roger. Would you mind sharing you whole “Block renderer” function/file you have in the Next.js. Looks like some code is missing from your example shown.