-
Notifications
You must be signed in to change notification settings - Fork 110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rich text images with fluid gatsby-image #70
Comments
I have images coming in by putting the following into my // gatsby-config.js
...
{
resolve: `@contentful/gatsby-transformer-contentful-richtext`,
options: {
renderOptions: {
renderNode: {
[BLOCKS.EMBEDDED_ASSET]: node => {
return `<img src="${node.data.target.fields.file['en-US'].url}" />`
}
},
},
},
},
... |
hey @codeithuman, |
I see, that makes sense, hence the name ‘ToHTML’. I will give the ‘to JSX’ library you linked a try. Thanks so much for the help and quick reply. I really appreciate your time @Khaledgarbaya. |
@Khaledgarbaya Can we reopen this as a feature request? It doesn't seem impossible for the Perhaps this isn't the right repo, but since Contentful is maintaining the Gatsby Source Contentful plugin, I'm not sure the correct place for this feature request. |
@jmdmacapagal So the way I got it to work for me was to utilize the option in When enabled you'll be able to query all the images For my purposes i did a query for the post and I don't know if that's the most efficient way to do it but it's currently working :) |
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 |
Think this should be re-opened as the feature has not been built |
I agree, this is such a standard use case I'm very surprised to find out there isn't a recommended approach for doing this. Building a blog in contentful and having images in the content - I actually expected it to just work. @Khaledgarbaya
Here was my solution, I just mashed the url into the format react-image was expecting from the fragment. No idea if this is better or worse than @AnalogMemory 's solution |
I decided fixed images were better for my solution, this is what I ended up using.
I've seen similar performance impact to using gatsby-image the regular way. The blog I'm making has a width of 630px, so that's effectively what I'm using as a manual max-width. |
Is there any type of update on this? |
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 })
) |
@TomPridham thank you!! This is super helpful and I got it to work! For anyone looking to do the same, don't just blindly copy paste some of the code like I did. Don't forget the api returns |
Is there an official fix in the works? It's unclear which hacky solution we're supposed to use:
@codeithuman could you re-open? |
We're also using the first approach, it looks like this
Our I don't believe a better approach is available right now (?) [edit] - actually @TomPridham's approach looks good and even better on the bundle! |
Note that @TomPridham 's approach does not cover the recursive case of an entry embed that has an image field you want to use (as opposed to a directly embedded asset). This deserves its own PR.. Can we please re-open @codeithuman |
Hey Folks, Please let not forget that the react renderer needs to work also for plain react app. Also, not everyone is using My suggestion is to get the learnings from this issue and create a helper renderer library that support these cases and people can install seperatly. // PS: this is an imaginary code
import gatsby-renderer-options from 'gatsby-renderer-options'
//......
documentToReactComponents(
richText,
gatsby-renderer-options
) |
@Khaledgarbaya This issue is getting brought up here because the |
Discussion of this issue on Contentful Slack https://contentful-community.slack.com/archives/CBYTK7T9S/p1586228113005000 |
Why is this issue closed? Is there a proper way to handle gatsby-image in rich Text? |
@Khaledgarbaya would a Contentful gatsby image helper be possible solution? I guess similar to how sanity-source-plugin.
This makes it easy to work with gatsby images in Sanity's rich text. |
I think it might work. Exposing resolveFluid would allow us to pass in the asset data from rich text then we'll get that fluidProps back. Will create a prototype this weekend! |
Could we start by re-opening the issue? |
Alright I got it to work! I think this is the easiest and cleanest way to do it. So we can essentially use the So all we would have to do is // image shape is the same as the one returned by your assets in rich text. (Without the localization)
const fluidProps = resolveFluid(image, {}) But here's the gist for those interested in implementing this themselves while waiting for it to merge. Edit: fixed the gist link |
Thanks @daydream05 Will check out this approach and report back! |
@daydream05 can you please add the gist, the url returns 404 |
Fixed it! Sorry bout that. Here's the link as well: https://gist.github.com/daydream05/b5befd50f9c9001fb094f331f98a3ec5 |
@daydream05 this is a great solution, thanks so much! |
we should be seeing a fix from the team here: gatsbyjs/gatsby#25249 yahoo! |
Since I'm not super advanced, I'm a bit lost here. What's the proper way to handling this now, given the answer from @chang-ryan ? I am a bit lost following gatsbyjs/gatsby#25249 |
@Erythros the next version of Contentful can handle it but there’s no timeline yet on when it gets merged. But you can use it now and experiment with it. (I use it for production for 3 client sites and they seem to work fine)
But if you’re stuck with the current version. You can use the solution I linked. |
I highly recommend the next release, which should be merged in due time but is working great for us so far! |
Might as well wait for it. Is there any setup or configuration needed or should work out of the box by just doing this?
|
is the next version out? and what is the solution? |
Hello ! My graphql request : allContentfulBlogPost {
edges {
node {
id
title
content {
raw
references {
... on ContentfulAsset {
contentful_id
gatsbyImageData(layout: FULL_WIDTH, quality: 80, formats: [WEBP, AUTO], placeholder: BLURRED)
description
}
}
}
}
}
} My Post component : import React from "react"
import { GatsbyImage, getImage } from "gatsby-plugin-image"
import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { BLOCKS } from '@contentful/rich-text-types'
const richTextImages = {};
const options = {
renderNode: {
[BLOCKS.EMBEDDED_ASSET]: node => {
const imageData = richTextImages[node.data.target.sys.id];
const image = getImage(imageData.image)
return <GatsbyImage image={image} alt={imageData.alt}/>
},
},
}
const Post = ({ pageContext, location }) => {
const { post } = pageContext
post.content.references.map(reference => (
richTextImages[reference.contentful_id] = {"image": reference.gatsbyImageData, "alt": reference.description}
))
return (
<>
<h1>{post.title}</h1>
<div>{documentToReactComponents(JSON.parse(post.content.raw), options)}</div>
</>
)
}
export default Post I hope this will help ! |
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 myarticle-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.
The text was updated successfully, but these errors were encountered: