No embedded images with rich text but would like to use gatsby-image

I’m building a Gatsby site and am using the gatsby-source-contentful plugin as well as the @contentful/rich-text-react-renderer package to allow me to override the default html elements with React components.

Below is a very basic example of my usage, there are no overrides, and when the item is rendered everything looks fine but the embedded images are not present. They do not visibly show and are not in the DOM either.

FYI: This is TypeScript code

export const ContentfulRichText: FunctionComponent<ContentfulRichTextProps> = ({
  json,
}): ReactElement => {
  return <Fragment>{documentToReactComponents(json)}</Fragment>
}

Now, if I use my own components, like below, my components (or rather the ones from Theme UI) are rendered and still there are no images… :confused:

import React, { FunctionComponent, ReactElement, Fragment } from 'react'
import {
  documentToReactComponents,
  Options,
} from '@contentful/rich-text-react-renderer'
import { BLOCKS, INLINES } from '@contentful/rich-text-types'
import { ContentfulRichTextProps } from './ContentfulrichText.models'
import { Styled } from 'theme-ui'
import Img from 'gatsby-image'

const Hyperlink = (props): ReactElement => <Styled.a {...props} />

const options: Options = {
  renderNode: {
    [BLOCKS.HEADING_1]: (node, children): ReactElement => (
      <Styled.h1>{children}</Styled.h1>
    ),
    [BLOCKS.HEADING_2]: (node, children): ReactElement => (
      <Styled.h2>{children}</Styled.h2>
    ),
    [BLOCKS.HEADING_3]: (node, children): ReactElement => (
      <Styled.h3>{children}</Styled.h3>
    ),
    [BLOCKS.HEADING_4]: (node, children): ReactElement => (
      <Styled.h4>{children}</Styled.h4>
    ),
    [BLOCKS.HEADING_5]: (node, children): ReactElement => (
      <Styled.h5>{children}</Styled.h5>
    ),
    [BLOCKS.HEADING_6]: (node, children): ReactElement => (
      <Styled.h6>{children}</Styled.h6>
    ),
    [BLOCKS.PARAGRAPH]: (node, children): ReactElement => (
      <Styled.p>{children}</Styled.p>
    ),
    [BLOCKS.UL_LIST]: (node, children): ReactElement => (
      <Styled.ul>{children}</Styled.ul>
    ),
    [BLOCKS.OL_LIST]: (node, children): ReactElement => (
      <Styled.ol>{children}</Styled.ol>
    ),
    [BLOCKS.LIST_ITEM]: (node, children): ReactElement => (
      <Styled.li>{children}</Styled.li>
    ),
    [INLINES.HYPERLINK]: (node, children): ReactElement => {
      return <Hyperlink href={node.data.uri}>{children}</Hyperlink>
    },
  },
}

export const ContentfulRichText: FunctionComponent<ContentfulRichTextProps> = ({
  json,
}): ReactElement => {
  return <Fragment>{documentToReactComponents(json, options)}</Fragment>
}

I can add my own image component like below, which finally renders an image, but I feel like this shouldn’t be needed. Am I wrong? I certainly shouldn’t have to set the the src in this way, especially since I have multiple locales which would make this code very brittle.

[BLOCKS.EMBEDDED_ASSET]: (node, children): ReactElement => {
      return (
        <img
          key={node.data.target.fields.file.en.url}
          src={node.data.target.fields.file.en.url}
        />
      )
    },

I am also hoping to use gatsby-image which supposedly supports Contentful but I can’t see how to set it up for rich text…

I would appreciate any pointers from someone that has done this before.

Many thanks

@zander.martineau this github thread should help you. It’s towards the bottom.

Basically the idea is:

  • On your gatsby node, grab all the images from the rich text
  • pass it down to your template
  • use the images to filter allContentfulAsset where you can query fluid image
  • then match the image url from rtf with contentful asset.

The full code is in that thread. Just modify it to your use case.

Cheers!