question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Rich text images with fluid gatsby-image

See original GitHub issue

I’m having trouble figuring out how to get images from a Contentful rich text field working with fluid Gatsby images.

Specifically, what does the GraphQL query need to look like? Do I need to edit my gatsby-node.js allContentfulArticle query or do I need to add a query to my article-template.js file?

Also, do I need to use documentToHtmlString? If so, what element do I need to write a custom renderer for? Will it look similar to the Advanced Example found here?

Thanks for any and all help! Let me know what additional information is needed.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:35 (2 by maintainers)

github_iconTop GitHub Comments

44reactions
franklintartercommented, Dec 8, 2019

I got this to work using @AnalogMemory’s concept. Here’s the code example:

// RichTextRenderer.js
import React from "react";
import { BLOCKS } from "@contentful/rich-text-types";
import { documentToReactComponents } from "@contentful/rich-text-react-renderer";
import Image from "gatsby-image";
import { useContentfulImage } from "../../hooks";

const options = {
  renderNode: {
    [BLOCKS.EMBEDDED_ASSET]: node => {
      const fluid = useContentfulImage(
        node.data.target.fields.file["en-US"].url
      );
      return (
        <Image title={node.data.target.fields.title["en-US"]} fluid={fluid} />
      );
    }
  }
};

export default ({ richTextJson }) =>
  documentToReactComponents(richTextJson, options);

And the hook that connects the nodeId to the fluid image.

// useContentfulImage.js
import { graphql, useStaticQuery } from "gatsby";

export default assetUrl => {
  const { allContentfulAsset } = useStaticQuery(
    graphql`
      query CONTENTFUL_IMAGE_QUERY {
        allContentfulAsset {
          nodes {
            file {
              url
            }
            fluid(maxWidth: 1050, quality: 85) {
              ...GatsbyContentfulFluid_withWebp
            }
          }
        }
      }
    `
  );
  return allContentfulAsset.nodes.find(n => n.file.url === assetUrl).fluid;
};

Then in your blog post or wherever pass the rich text Json into the RichTextRenderer component <RichTextRenderer richTextJson={bodyTest.json} />

14reactions
TomPridhamcommented, Feb 12, 2020

we were able to solve this recently. using @AnalogMemory 's solution didn’t work for us because it was causing a huge json blob to be included in our bundle. this removes the manual process of creating the urls and doesn’t require including all of the contentful assets

/* gatsby-node.js */
const { get } = require('lodash')

const getImagesFromRichText = edge =>
  get(edge, 'node.body.json.content', []).reduce((acc, c) => {
    const url = get(c, 'data.target.fields.file.en.url')
    if (c.nodeType == 'embedded-asset-block' && url) {
      return [...acc, url]
    }
    return acc
  }, [])

const blogPostData = await graphql(`
  query BlogPostData {
    allContentfulBlogPost {
      edges {
        node {
          slug
          body {
            json
          }
        }
      }
    }
  }
`)

const posts = blogPostData.data.allContentfulBlogPost.edges
posts.forEach((post, index) => {
  const images = getImagesFromRichText(post)

  createPage({
    path: `${pathPrefix}${post.node.slug}/`,
    component,
    context: {
      images,
      slug: post.node.slug,
    },
  })
})

exports.createPagesStatefully = async function({ actions }) {
  const { createPage } = actions

  await createBlogs({
    helpers: { createPage },
    path: '/blogs/',
    component: require('src/templates/blog'),
  })
}

/* src/templates/blog.ts */

import get from 'lodash/get'
import { convertRichText } from 'components/Markup'

export const pageQuery = graphql`
  query BlogPostPageQuery($slug: String!, $images: [String!]!) {
    contentfulBlogPost(slug: { eq: $slug }) {
      slug
      body {
        json
      }
    }
    allContentfulAsset(filter: { file: { url: { in: $images } } }) {
      edges {
        node {
          fluid(maxWidth: 700, quality: 85) {
            ...GatsbyContentfulFluid_withWebp
          }
        }
      }
    }
  }
`

const PostPage: React.SFC<PostPageProps> = props => {
  const { data } = props

  const imageNodes = data.allContentfulAsset.edges || []
  const images = imageNodes.map(edge => edge.node.fluid)
  const richText: RichDocument = get(bodyData, 'json')

  return (
    <div>
      {richText &&
        convertRichText({
          richText,
          images,
        })}
    </div>
  )
}

export default PostPage

/* src/components/markup/index.ts */

import { documentToReactComponents } from '@contentful/rich-text-react-renderer'
import get from 'lodash/get'
import Image from 'src/components/Image'

const nodeRenderer = ({ images }) => (
  {
    renderNode: {
      /**
       * Render Images
       */
      [BLOCKS.EMBEDDED_ASSET]: (node: Block) => {
        if (node.data.target) {
          const { title, description, file } = get(
            node,
            'data.target.fields',
            {}
          )
          // image.src has a url param that we need to strip off to match file.url
          <Image
            src={file.url}
            fluid={images.find(
              image => image.src.split('?')[0] === file.url
            )}
          />
        }
      },
      // ...other rendering functions
    },
  }
)

export const convertRichText = ({
  images,
  richText,
}: ConvertRichTextArgs): React.ReactNode =>
  documentToReactComponents(
    richText,
    nodeRenderer({ images })
  )
Read more comments on GitHub >

github_iconTop Results From Across the Web

No embedded images with rich text but would like to use ...
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 ...
Read more >
Gatsby Source Contentful 4.x - Rich Text - Stack Overflow
I have a gatsby app for which I am using contentful rich text. contentful recently made some breaking changes to the way content...
Read more >
Images and Media - Gatsby
Pull images, video, GIF, and other media into your site. ... Gatsby offers tools to create rich experiences with gatsby-image, while preventing performance ......
Read more >
Rendering Gatsby Images from Contentful Rich Text
Gatsby is great at rendering images. To do this, you need to leverage the Gatsby Image Plugin. For most of the images on...
Read more >
Image as a Link with Contentful and Gatsby JS
As rich as the Rich Text functionality of Contentful might be, ... Rich Text elements in Gatsby ;) I chose to use a...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found