GATSBY CMS
VISUAL EDITOR
WITH md0
md0 edits your MDX and markdown files directly in GitHub. Your Gatsby GraphQL queries keep working unchanged. No new gatsby-source plugins. No API keys. No build-time data fetching from an external CMS.
Context
Two Ways to Manage Content in Gatsby
Gatsby supports two fundamentally different content approaches. Knowing which one your project uses determines whether md0 is the right tool.
File-Based (md0 Works Here)
Markdown or MDX files live in the repo. gatsby-source-filesystem reads them. GraphQL queries fetch the content at build time.
md0 edits these files directly in GitHub. Gatsby picks up the changes on the next build. No additional plugins or API calls needed.
API-Sourced (md0 Does Not Apply)
Content lives in a headless CMS like Contentful, Sanity, or DatoCMS. Gatsby fetches it via a source plugin at build time.
md0 targets file-based content only. If your Gatsby site pulls all content from an external API, md0 is not the right fit.
How to Tell Which You Have
Open your gatsby-config.js. Look for gatsby-source-filesystem entries pointing to local directories. If you see entries like the ones below, your content is file-based and md0 works:
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'posts',
path: './content/posts',
},
}Before You Start
Prerequisites
A Gatsby site on GitHub with gatsby-source-filesystem
Your project must use gatsby-source-filesystem to read markdown or MDX files from a local directory. This is standard for Gatsby blog and docs templates.
Content in a /content/ or /posts/ directory
md0 needs a clear directory to map to. Common Gatsby structures use content/posts/, content/blog/, or src/content/. Any consistent directory works.
An md0 account
Free for public repositories. Sign up at cms.md0.io using GitHub OAuth.
Gatsby MDX Structure
How Gatsby Reads MDX Files
Gatsby reads MDX files at build time via the gatsby-source-filesystem plugin. The plugin scans directories you declare in gatsby-config.js, reads every file it finds, and makes that content available through GraphQL as nodes.
gatsby-config.js - filesystem source plugin
module.exports = {
siteMetadata: {
title: 'My Gatsby Site',
siteUrl: 'https://example.com',
},
plugins: [
// Source filesystem reads your content directory
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'posts',
path: `${__dirname}/content/posts`,
},
},
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'docs',
path: `${__dirname}/content/docs`,
},
},
// MDX transformer converts .mdx files to GraphQL nodes
'gatsby-plugin-mdx',
],
}Sample MDX post - content/posts/my-first-post.mdx
--- title: "My First Post" date: "2026-01-15" slug: "my-first-post" description: "An introduction to my Gatsby blog." image: "/images/hero.jpg" tags: ["gatsby", "mdx", "tutorial"] published: true --- This is the post content. Write in standard Markdown. You can also use JSX components inline when using MDX: <Callout>This is an MDX component.</Callout>
What md0 Maps To
In md0, you create a collection that points to the same path declared in your gatsby-config.js. If your config says path: ./content/posts, your md0 collection path is content/posts.
md0 reads the MDX files in that directory, shows them in the editor, and commits changes back to the same path. Gatsby reads the same directory at build time. The two never conflict because md0 runs before the build and Gatsby runs after.
Step 1
Connect Your GitHub Repository
Sign in with GitHub OAuth
Go to cms.md0.io and click Sign in with GitHub. Grant md0 access to the specific repositories you want to manage. You do not need to grant access to all repos.
md0 requests read and write access to repository contents. This is required to read your existing MDX files and commit changes when editors save.
Select Your Gatsby Repo and Branch
After authentication, pick your Gatsby project from the repository list. Select the branch you deploy from, typically main or master.
Once connected, md0 scans your repo and lists all directories containing .md and .mdx files. It also reads the front matter field names from your existing files to suggest a starting schema.
Step 2
Map Collections to Your gatsby-config Paths
Each collection in md0 maps to a directory that gatsby-source-filesystem already watches. The path in md0 must match the path in your Gatsby config exactly.
Example: posts collection schema
{
"name": "posts",
"path": "content/posts",
"extension": "mdx",
"fields": [
{ "name": "title", "type": "text", "required": true },
{ "name": "date", "type": "date", "required": true },
{ "name": "slug", "type": "text" },
{ "name": "description", "type": "textarea" },
{ "name": "image", "type": "text" },
{ "name": "tags", "type": "text" },
{ "name": "published", "type": "boolean", "default": false }
]
}Match the Path Exactly
If your gatsby-config.js declares:
path: `${__dirname}/content/posts`Then your md0 collection path must be:
"path": "content/posts"
A path mismatch is the most common setup error. md0 writes to content/posts/ but if Gatsby is watching src/content/posts/, the build does not pick up the new files.
Common Gatsby Content Structures
content/postsBlog posts (gatsby-starter-blog default)content/blogAlternative blog pathcontent/docsDocumentation pagessrc/contentContent inside src directoryStep 3
GraphQL Queries Keep Working Unchanged
md0 does not touch your GraphQL layer at all. Your existing allMdx and allMarkdownRemark queries continue reading from the same file paths. md0 just updates what those files contain.
GraphQL query - list all posts
query AllPostsQuery {
allMdx(
sort: { frontmatter: { date: DESC } }
filter: { frontmatter: { published: { eq: true } } }
) {
nodes {
id
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
slug
description
image
tags
}
excerpt
}
}
}Front Matter Field Names Must Match
Your GraphQL query references front matter fields by name. The field names in your md0 schema must match exactly what your GraphQL queries use. If your query reads frontmatter.date, your md0 schema field must be named date.
md0 writes only the fields defined in your schema. If a field in your GraphQL query is not in the md0 schema, it will not appear in files created through md0. This causes that field to resolve as null in your GraphQL response for those files.
GraphQL query - single post by slug (gatsby-node.js)
// In gatsby-node.js - createPages
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
allMdx {
nodes {
frontmatter {
slug
}
internal {
contentFilePath
}
}
}
}
`)
result.data.allMdx.nodes.forEach((node) => {
createPage({
path: `/blog/${node.frontmatter.slug}`,
component: `${postTemplate}?__contentFilePath=${node.internal.contentFilePath}`,
context: {
slug: node.frontmatter.slug,
},
})
})
}When md0 creates a new post and commits it to GitHub, the next Gatsby build finds the new file, runs it through gatsby-source-filesystem and gatsby-plugin-mdx, and createPages generates a new route for it. No code changes required.
Step 4
Build on Commit
Gatsby generates static HTML at build time. Every time md0 commits a content change to your GitHub repo, your deployment platform triggers a new build. The updated content goes live once the build completes.
Netlify
Connect your GitHub repo to Netlify and set the build command and publish directory. Netlify builds on every push automatically.
Netlify build settings
Build command: gatsby build Publish directory: public
Vercel
Vercel detects Gatsby projects automatically. Import the repo and Vercel sets the build command and output directory. Every push triggers a deployment.
Vercel build settings (auto-detected)
Build command: gatsby build Output directory: public
The Full Publish Flow
Build Times
Gatsby build times grow with content volume. A site with 100 posts typically builds in 30 to 90 seconds on Netlify or Vercel. If build times become a bottleneck, look at Gatsby Cloud (now part of Netlify) for incremental builds that only rebuild changed pages.
Troubleshooting
Common Issues
New posts do not appear after md0 saves
The most likely cause is a path mismatch. Open your gatsby-config.js and check the path value in the gatsby-source-filesystem block. Compare it to your md0 collection path. They must be identical.
Also check whether the build actually ran. Go to your Netlify or Vercel dashboard and confirm a deploy was triggered after the md0 commit.
GraphQL query returns null for new fields
If you add a new field to your md0 schema that does not exist in any current files, Gatsby's GraphQL schema may not include it yet. Gatsby infers the GraphQL schema from your existing files. Add the new field to at least one existing file manually, then rebuild. After that, md0 can populate the field in new posts.
MDX JSX components not rendering
md0 edits the MDX source file exactly as you write it. If you use JSX components inside MDX content (like <Callout> or <YouTube id="..." />), md0 preserves them as text. The visual editor shows the JSX syntax literally. Gatsby processes the JSX at build time when it compiles the MDX. This is expected behavior.
Build fails with a createPages error
A createPages error after an md0 save usually means a required front matter field is missing from the new file. Check your gatsby-node.js and confirm that every field your page creation logic reads is included in your md0 schema as a required field.
Published filter not working
If you use filter: { frontmatter: { published: { eq: true } } } in your GraphQL query, make sure the published field in your md0 schema is a boolean type with a default of false. Files created through md0 will have published: false until an editor explicitly toggles it on.
Compatibility
Works With
gatsby-plugin-mdx
Works with gatsby-plugin-mdx v3 and v4. md0 writes standard MDX files. The plugin processes them at build time. No configuration changes needed.
gatsby-transformer-remark
If your project uses gatsby-transformer-remark instead of MDX, set your md0 collection extension to md. md0 writes standard markdown with YAML front matter, which is exactly what allMarkdownRemark queries expect.
Netlify and Vercel
Both platforms rebuild on every GitHub push. md0 commits trigger deployments automatically. Build command: gatsby build. Publish directory: public.
Gatsby Starters
Works with gatsby-starter-blog, gatsby-starter-default, gatsby-blog-starter, and other file-based starters. If the starter reads content from gatsby-source-filesystem, md0 works with it.
Keep Going
Next Steps
Learn the Visual Editor
md0's visual markdown editor outputs standard GitHub-flavored markdown and MDX. Editors get formatting controls, image uploads, and a live preview without seeing raw markup.
Visual markdown editor →Use md0 with Other Frameworks
md0 works with any static site generator that reads markdown from a Git repo.
Comparing Your Options?
- Coming from Contentful? See the comparison →
- Comparing md0 to Netlify CMS? Netlify CMS alternative →
CONNECT YOUR
GATSBY REPO
Free for public repositories. No GraphQL changes. No new source plugins. Set up in under five minutes.
CONNECT YOUR GATSBY REPO