Add support for images embedded in the markdown content #122
Conversation
Signed-off-by: Maciek Sawicki <maciek@decent.com>
Thanks it works like you said. With the strapi gatsby tutorial modified chunk codes are :
{
resolve: "gatsby-source-strapi",
options: {
apiURL: process.env.API_URL || "http://localhost:1337",
contentTypes: [
// List of the Content Types you want to be able to request from Gatsby.
"article",
"category",
],
markdownImages: {
typesToParse: {
article: ['content']
}
},
queryLimit: 1000,
},
},
import Img from "gatsby-image"
export const query = graphql`
query ArticleQuery($id: Int!) {
strapiArticle(strapiId: { eq: $id }) {
strapiId
title
content
content_images {
childImageSharp {
original {
src
}
fluid {
...GatsbyImageSharpFluid
}
}
base
}
published_at
image {
publicURL
}
}
}
` <ReactMarkdown source={article.content}
renderers={{
image: ({ src, alt }) => {
const image = article.content_images.find(element => element.base === src );
console.log(image)
return <Img fluid={image.childImageSharp.fluid} alt={alt} />
}
}}
/> Thanks again, that's really nice to have gatsby processing inlined image from the strapi editor. Let's hope it will be merged! |
Awesome |
@unagigd just a quick question, do you know by chance why the Babel compilation step has to be done manually when I import your fork in Gatsby? |
AFAIK Gatsby is not running any scripts inside the local |
I'm very new to gatsby & strapi, so I don't know if I did it right
`There was an error in your GraphQL query:
and when I change it back to strapi version it works fine. |
Thanks for this! I was being asked to provide this functionality and had no idea where to begin as I'm still learning my way around. |
Is this idea going to be merged any time soon? I think this plugin desperately needs this implementation. |
We're waiting for merged! |
Still waiting :'( |
Is there any update on when this will get approved/merged? I need this to migrate my content into Strapi. Thanks! |
Any updates on this ? Is it merged ? |
Here, I'm waiting for merge too. |
while this is a reasonable approach IMHO it would be better to perform downloading of images and rewriting at the time of the node creation (e.g. check how gatsby-source-wordpress-experimental does it). |
Another option would be to try to do it similar to here hygraph/gatsby-source-graphcms#44 (comment) |
Hi, how are you creating "content_images"? |
Could somebody review that? |
There is little left to make the first anniversary... |
Looking forward to this, anybody knows if it's planned to be merged soon ? |
It would be awesome if we also could add metadata like width and height. |
for people who think will do it yourself. same like @unagigd did on this PR, and want to add some fields such as Sorry if it's not polite. I just want to share from my experience, first I force this update with add package
const fetch = require('node-fetch')
const commonmark = require('commonmark')
const reader = new commonmark.Parser()
const { createRemoteFileNode } = require(`gatsby-source-filesystem`)
module.exports.onCreateNode = async ({ node, actions, store, cache, createNodeId, touchNode, createContentDigest }) => {
const { createNodeField, createNode } = actions
// in this case Blog Content Type
if (node.internal.type === "StrapiBlog") {
const parsed = reader.parse(node.content)
const walker = parsed.walker()
let event, nd
while ((event = walker.next())) {
nd = event.node
// process image nodes
if (event.entering && nd.type === 'image') {
let fileNodeID, fileNodeBase
const filePathname = nd.destination
// using filePathname on the cache key for multiple image field
const mediaDataCacheKey = `strapi-media-content-${filePathname}`
const cacheMediaData = await cache.get(mediaDataCacheKey)
// If we have cached media data and it wasn't modified, reuse
// previously created file node to not try to redownload
if (cacheMediaData) {
fileNodeID = cacheMediaData.fileNodeID
fileNodeBase = cacheMediaData.fileNodeBase
touchNode({ nodeId: cacheMediaData.fileNodeID })
}
const source_url = `${filePathname.startsWith('http') ? '' : process.env.API_URL || 'http://localhost:1339'}${filePathname}`
if (!fileNodeID) {
try {
const fileNode = await createRemoteFileNode({
url: source_url,
store,
cache,
createNode,
createNodeId,
})
// If we don't have cached data, download the file
if (fileNode) {
fileNodeID = fileNode.id
fileNodeBase = fileNode.base
await cache.set(mediaDataCacheKey, {
fileNodeID,
fileNodeBase,
})
}
} catch (e) {
console.log(e)
// Ignore
}
}
if (fileNodeID) {
// create an array of parsed and downloaded images as a new field
if (!node['content_images___NODE']) {
node['content_images___NODE'] = []
}
node['content_images___NODE'].push(fileNodeID)
node['content'] = node['content'].replace(filePathname, fileNodeBase)
}
// get rest image and make fields
try {
const params = `?url=${source_url}`
const restFile = (await (await fetch(`${process.env.API_URL || 'http://localhost:1339'}/upload/files${params}`)).json())[0]
const restFileId = createNodeId(restFile.id)
const restFileWithFileNodeId = {
...restFile,
fileNodeId: fileNodeID, // refer to file id
}
// create rest fields node
const create = await createNode({
...restFileWithFileNodeId,
id: restFileId,
internal: {
type: 'RestImagesMarkdown',
contentDigest: createContentDigest(restFileWithFileNodeId)
}
})
if (restFileId && create) {
// create an array of parsed and downloaded images as a new field
if (!node['content_rest_images___NODE']) {
node['content_rest_images___NODE'] = []
}
node['content_rest_images___NODE'].push(restFileId) // nodeId
}
} catch (error) {
console.log('getSourceFileMedia', error)
}
}
}
}
}; use Query export const query = graphql`
query BlogBySlug {
strapiBlog {
id
slug
title
image {
...standardImage
}
content
content_images {
...standardImage
base
id
}
content_rest_images {
id
fileNodeId
alternativeText
caption
}
}
}
`
const renderers = {
image: ({ src }) => {
const blog = props.data.strapiBlog
const image = blog.content_images.find(e => e.base === src );
const rest = blog.content_res_images.find(e => e.fileNodeId === image.id)
return (
<ImageParagraph>
<FluidImg
style={{ width: '100%' }}
imgStyle={{ objectFit: 'contain' }}
loading="lazy" image={image} alt={rest.caption}
/>
<p>source: {rest.caption}</p>
</ImageParagraph>
)
}
} but thanks for this PR, I learn about gatsby node works. thanks @unagigd ⚡️ |
1000 years later: Resolved conflicts and a merge would be nice 🤷♂️ idk |
Hi. I was looking at the state of this PR, but at some point I gave up. I couldn't get any approving review (from people with write or admin permissions), so I couldn't merge it. Since then my project moved from Strapi. |
@unagigd moved to where? which tool? |
That is a shame as this seems like a great feature. Are there any other work arounds that people have found? |
@alexandrebodin What is currently preventing this being merged in? |
@unagigd that's a genuine pity... I hope your project is booming! @OddBlueDog The only other workaround I can think of that wouldn't be dev intensive is to retrieve the images from a CDN. At least you could then still get the nice features of lazy loading and traced images. |
The same thing happens to me. Does someone have a current fork / branch I could please install this unmerged version from? I was really counting on being able to have images in the markdown :/ |
I have published this fork to https://www.npmjs.com/package/@oddbluedog/gatsby-source-strapi-support-markdown-images Remove gatsby-source-strapi from package.json and replace it with: Also change your gatsby-config to use |
@OddBlueDog I've implemented your published fork on my project. I haven't done this before and I wanted to ask if there are any drawbacks or things to keep in mind when using it. Like if gatsby-source-strapi gets updated, will I not be able to update my project? Thanks. |
The forked version will keep working with strapi, unless they make some kind of breaking change to their graphql implementation which I think is unlikely. If a new version of gatsby-source-strapi comes out then this fork will also carry on working, but there might be new features in the latest version that aren’t in the fork. Hopefully they add markdown image support to a future version of the official package and then we can all switch back! :-) I’ve added one another fix to my fork and a couple of package updates. I do really like strapi but it seems like this project for gatsby has been forgotten a bit currently. |
Solution if you are using react-markdown to convert md to html:
This might not be good for SEO, but will get the job of showing images done. |
This is a very big issue. We need this feature. How do you write blog's article ? Like text1, photo1, text2, photo2, ... ? |
I ended up using a repeatable component like:
So, in Strapi, each "section" of my blog post can have a title, image and/or rich text block. And in the React side, I loop through all the sections, making sure to check if the block has content in the relevant part: title (h3), image (full width or max-width), description (markdown). |
Hi there, sorry for jumping in this thread but I have added support for markdown images in this plugin if you're keen on using them like we are. https://github.com/relate-app/gatsby-source-strapi |
That repository is not available. |
Gatsby in v4 stops allowing to mutate nodes so the solution from @OddBlueDog became invalid. I made small changes to his code, but I don't have time to prepare a generic plugin :( Feel free to modify and release:
|
It is now, wasn't aware it was private |
This PR includes an idea to solve the gap in image processing. When we use rich content including images those images are not downloaded and not processed by Gatsby during the build. Instead their URLs are untouched and if they are uploaded on the local instance of Strapi (/uploads folder) there is no way to display them on the final static page.
The idea is to:
content
the images would go tocontent_images
With that all the files will be copied to Gatsby app.
Proposed configuration could look like this:
We're currently using this in our project and it works just fine.
In our case we're replacing all the images from the markdown with
gatsby-image
, like this (not a real code):Thought it may be useful.
#114