gatsby-source-strapi
gatsby-source-strapi copied to clipboard
Parse markdown to mdx?
Is it possible to make use of mdx when pulling markdown content? The main idea is that I'd like to create a sort of "shortcodes" setup to where when the content is pulled in it will use the components the content editor defined in the markdown (mdx style) in strapi.
This would work by adding a globalScope
for mdx
module.exports = {
plugins: [
{
resolve: `gatsby-mdx`,
options: {
globalScope: `import { SketchPicker } from 'react-color';
export default { Picker: SketchPicker }`
}
}
]
};
So then if a content type had a content body with:
<Picker />
It would then render that component that exists within Gatsby as long as the content is parsed with mdx.
Is this possible?
reference: https://www.christopherbiscardi.com/post/towards-shortcodes-for-gatsby-sites/#introducing-globalscope
Also a video of using DatoCMS with mdx: https://www.youtube.com/watch?v=Cwarh7qpEe0
Proof on Concept
I've created PoC of that. It assumes that:
- There is model called
article
in Strapi -
article
model hascontent
, which is type ofrich text
- You have
gatsby-plugin-mdx
enabled
Then, in your gatsby-node.js
, create onCreateNode
function, which basically does three things:
- Checks if node type is
StrapiArticle
- Creates new node with
mediaType
equal totext/markdown
- it's crucial to do so, becausegatsby-plugin-mdx
now will be able to parse content as MDX - Links this new node with current node
So it looks like this:
module.exports.onCreateNode = async ({ node, actions, createNodeId }) => {
if (node.internal.type === "StrapiArticle") {
const newNode = {
id: createNodeId(`StrapiArticleContent-${node.id}`),
parent: node.id,
children: [],
internal: {
content: node.content || " ",
type: "StrapiArticleContent",
mediaType: "text/markdown",
contentDigest: crypto
.createHash("md5")
.update(node.content || " ")
.digest("hex"),
},
};
actions.createNode(newNode);
actions.createParentChildLink({
parent: node,
child: newNode,
});
}
};
Now you are able to do similar GraphQL query:
query MyQuery {
allStrapiArticle {
nodes {
id,
title,
childStrapiArticleContent {
childMdx {
body
}
}
}
}
}
After that, you can use it in the pages, even with shortcodes
, like this:
{allStrapiArticle.nodes.map((article, i) => (
<article key={i}>
<header>
<h2>{article.text}</h2>
</header>
<section>
<MDXProvider
components={{
Hi: props => <h3 {...props} />,
}}
>
<MDXRenderer>
{article.childStrapiArticleContent.childMdx.body}
</MDXRenderer>
</MDXProvider>
</section>
</article>
))}
Of course, in Strapi, create an article with the content. Mine looked like this:
# Hello
<Hi>I'm an shortcode!</Hi>
Future work
Since there is no API to Strapi to get model, I'd place definition of "markdownable" content in the plugin configuration. So for example:
{
resolve: "gatsby-source-strapi",
options: {
apiURL: "http://localhost:1337",
contentTypes: [
{
type: "article",
markdownFields: ["content"],
},
"user",
],
queryLimit: 1000,
},
},
If I'll find some time for that, I can submit a PR for this feature.
CC?: @lauriejim
This is great @rozpuszczalny! I'll give it a go.
btw @rozpuszczalny this worked perfectly
imported these two
import { MDXProvider } from "@mdx-js/react"
import { MDXRenderer } from "gatsby-plugin-mdx"
There actually is an API to get the content type schema: /content-type-builder/content-types
.
It should be possible to query this information during startup and then create the correct sub nodes as https://github.com/strapi/gatsby-source-strapi/issues/89#issuecomment-559731259 showed (Related to #5)
Hey guys... what's the progress of this feature? It'll be amazing having such a thing like this
Any progress in this feature. Currently using onCreateNode
Hi there, thank you to @rozpuszczalny for the POC. The creation of nodes works very well. I am trying to create pages programmatically from the model that has mdx content. I have used the POC to create the nodes, and that works very well. But when I add the create pages code, I run into an error. My gatsby-node.js file:
const path = require(`path`);
const crypto = require(`crypto`)
exports.onCreateNode = async ({ node, actions, createNodeId }) => {
if (node.internal.type === "StrapiPublications") {
const newNode = {
id: createNodeId(`StrapiPublications-${node.slug}`),
parent: node.slug,
children: [],
internal: {
content: node.Content || " ",
type: "StrapiPublicationsContent",
mediaType: "text/markdown",
contentDigest: crypto
.createHash("md5")
.update(node.Content || " ")
.digest("hex"),
},
};
actions.createNode(newNode);
actions.createParentChildLink({
parent: node,
child: newNode,
});
}
};
const makeRequest = (graphql, request) => new Promise((resolve, reject) => {
// Query for nodes to use in creating pages.
resolve(
graphql(request).then(result => {
if (result.errors) {
reject(result.errors)
}
return result;
})
)
});
// Implement the Gatsby API “createPages”. This is called once the
// data layer is bootstrapped to let plugins create pages from data.
exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions;
const getPages = makeRequest(graphql, `
{
allStrapiPublications {
edges {
node {
id
slug
}
}
}
}
`).then(result => {
// Create pages for each article.
result.data.allStrapiPublications.edges.forEach(({ node }) => {
createPage({
path: `/${node.slug}`,
component: path.resolve(`src/templates/publication.js`),
context: {
id: node.id
},
});
})
});
// Query for articles nodes to use in creating pages.
return getPages;
};
When I run gatsby develop (having already developed the site previously), I receive this error:
There was an error in your GraphQL query:
Cannot query field "childStrapiPublicationsContent" on type "StrapiPublications".
If you don't expect "childStrapiPublicationsContent" to exist on the type "StrapiPublications" it is most
likely a typo.
However, if you expect "childStrapiPublicationsContent" to exist there are a couple of solutions to common
problems:
- If you added a new data source and/or changed something inside gatsby-node.js/gatsby-config.js, please try
a restart of your development server
- The field might be accessible in another subfield, please try your query in GraphiQL and use the GraphiQL
explorer to see which fields you can query and what shape they have
- You want to optionally use your field "childStrapiPublicationsContent" and right now it is not used
anywhere. Therefore Gatsby can't infer the type and add it to the GraphQL schema. A quick fix is to add at
least one entry with that field ("dummy content")
It is recommended to explicitly type your GraphQL schema if you want to use optional fields. This way you
don't have to add the mentioned "dummy content". Visit our docs to learn how you can define the schema for
"StrapiPublications":
https://www.gatsbyjs.com/docs/reference/graphql-data-layer/schema-customization#creating-type-definitions
File: src\templates\publication.js:28:9
failed extract queries from components - 0.321s
I can clear this error by using the gatsby clean command. Next time I run gatsby develop or gatsby build, no error occurs. But if I run it again (without the gatsby clean command), the same error comes up.
Any ideas on how to resolve this issue? My thinking is that when the site is developed/built from cache, the nodes haven't been created yet. But I am not sure how to tell gatsby to wait until node creation to build the pages. I am trying to debug using gatsby's documentation here: async lifecycles and gatsby node apis
Thanks very much in advanced!
Edit: I figured out the issue: the parent id is important, I set it back to parent: node.id and it works.
For those interested, when using Strapi v4 all rich text nodes will get automatically the text/markdown
media type. No need to use the onCreateNode method as described above. Just add gatsby-plugin-mdx
and you will find the childMdx
nodes in GraphQL. Just make sure you use V3 and not V4 of the gatsby-plugin-mdx
plugin as:
"Loading MDX from other sources as the file system is not yet supported in gatsby-plugin-mdx@^4.0.0"
I'm using this in combination with Gatsby V4 and its working fine.
Thanks for your interest in this project. This plugin is moving into the Gatsby User Collective and this repo will be archived. Please open an issue in that repository, submit a PR if you'd like to see this implemented, or join us on Discord if you have questions!