Contentful Embedded Content in Gatsby
This is specifically for Gatsby as that’s what I was using, but I suppose the implementation will be similar in other React/GraphQL environments.
When using @contentful/rich-text-react-renderer to render a Contentful entry, they suggest passing a custom rendering component for embedded entries.
However, I was unable to get a fully populated node
in the render handlers that would contain all the fields I require, with only access to the contentful_id
and the entry type. Whatever I fetched in graphql was available in the references
key of the content body
, but it wasn’t being sent on. Didn’t bother to check how this works under the hood, but managed to achieve what I wanted by passing the references
object to the custom renderer and searching for the relevant entry by contentful_id
.
import { documentToReactComponents } from '@contentful/rich-text-react-renderer'; | |
import { graphql } from 'gatsby'; | |
import React from 'react'; | |
// Custom renderer for embedded content which accepts `body` / `references` from the graphql query. | |
// Search `references` for the embedded object ID for full access to nested content. | |
const rendererOptions = (references) => ({ | |
renderNode: { | |
[BLOCKS.EMBEDDED_ASSET]: (node) => { | |
const imageID = node.data.target.sys.id; | |
const { | |
file: {url}, | |
title | |
} = references.find(({contentful_id: id}) => id === imageID); | |
return <img src={url} alt={title} />; | |
}, | |
[BLOCKS.EMBEDDED_ENTRY]: (node) => { | |
const entryID = node.data.target.sys.id; | |
const { title, slug } = references.find(({contentful_id: id}) => id === entryID); | |
return <a href={`/${slug}`}>{title}</a>; | |
}, | |
}, | |
}); | |
const Post = ({ data }) => { | |
const { post } = data; | |
return ( | |
<article> | |
{documentToReactComponents( | |
JSON.parse(post.body.raw), | |
rendererOptions(post.body.references) | |
)} | |
</article> | |
); | |
}; | |
export default Post; | |
// Include all required fields for the content references. For GatsbyJS at the least, `contentful_id` and `__typename` are required. | |
export const query = graphql` | |
query Post($id: String) { | |
post: contentfulBlog(id: { eq: $id }) { | |
body { | |
raw | |
references { | |
... on ContentfulAsset { | |
contentful_id | |
__typename | |
title | |
file { | |
url | |
} | |
} | |
... on ContentfulArticle { | |
contentful_id | |
__typename | |
title | |
slug | |
} | |
} | |
} | |
} | |
} | |
`; |