I built a blog with Gatsby and Tailwind using markdown for blog posts. When querying the data using graphql, I can’t get any of the images to display. My images are stored within the src/
directory in an images/
folder. My posts are inside of a pages/
folder within that same directory. At one point the site displayed ALL of the images including the ones in markdown, but wouldn’t work when deployed to Netlify. I’ve gotten the site to deploy, but now none of my images are showing and I’ve tried everything to get them back. I’ve also tried using gatsby-image (fixed and fluid) and still nothing.
This is my first project with gatsby, markdown, and graphql. Currently, the deployed site nikkipeel.netlify.app shows the proper path on the blog page for posts when inspected using devtools (../images/example.jpg), but still no image actually displays. 🤷♀️
Github repo: gatsby-blog
project structure:
|-- /public
|-- /src
|-- /components
|-- /pages
|-- /post-one
|--index.md
|-- /post-two
|--index.md
|-- /post-three
|--index.md
|-- /templates
|-- blog-post.js
|-- /images
|-- /static
|-- gatsby-config.js
|-- gatsby-node.js
|-- gatsby-ssr.js
|-- gatsby-browser.js
frontmatter for a blog post (index.md) –
---
path: "/create-a-blog-with-react-and-sanity"
date: "2021-01-10"
title: "Create a blog with React and Sanity"
description: "Create a technical blog built with React, TailwindCSS, and Sanity then deploy it using Github and Netlify"
category: "React"
author: "Nikki Peel"
authorImage: ../images/selfie.png
image: ../images/sanity-blog-collage.jpg
---
Blog.js with Graphql query for displaying all posts:
import React from "react"
import { graphql } from 'gatsby'
import Layout from "../components/layout"
import Link from 'gatsby-link';
import SEO from "../components/seo"
const BlogPage = ({ data }) => (
<Layout>
<SEO title="Blog" />
<main className="bg-brown text-white h-auto p-12">
<section className="container mx-auto">
<h1 className="text-4xl mono pl-4">Latest Blog Posts</h1>
<h2 className="text-lg mb-12 pl-4">Welcome to my page of blog posts</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 justify-center mx-auto md:gap-8 mb-24">
{data.allMarkdownRemark.edges.map(post => (
<article>
<Link to={post.node.frontmatter.path}>
<div className="block lg:h-64 relative leading-snug" key={ post.node.id }>
<img src={post.node.frontmatter.image} alt="" className="lg:block w-full items-center lg:h-full lg:object-cover lg:object-top relative lg:absolute"/>
<span className="block relative lg:h-full lg:flex lg:items-end ">
<h3 className="lg:bg-gray-800 lg:bg-opacity-75 text-white text-xl font-semibold lg:px-3 lg:py-4 rounded text-left">{post.node.frontmatter.title}</h3>
</span>
<div className="mt-4 mb-8">
<p className="text-base mb-4">{post.node.frontmatter.description}</p>
<span id={post.node.frontmatter.category} className="text-black font-semibold text-sm py-2 px-4 mr-2 rounded">{post.node.frontmatter.category}</span>
<small className="text-base ml-2">📅 {post.node.frontmatter.date}</small>
</div>
</div>
</Link>
</article>
))}
</div>
</section>
</main>
</Layout>
)
export const pageQuery = graphql`
query BlogIndexQuery {
allMarkdownRemark (sort: { fields: [frontmatter___date], order: DESC }){
edges {
node {
id
frontmatter {
path
title
description
date
category
image
}
}
}
}
}
`;
export default BlogPage;
And for Single Posts – blog-post.js:
import React from 'react';
import { graphql } from 'gatsby'
import Link from 'gatsby-link';
import Layout from '../components/layout';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
export default function Template({ data }) {
const post = data.markdownRemark
return (
<Layout>
<main className="container bg-brown min-h-screen p-4 md:p-12">
<Link to="/blog" exact className="text-white text-base items-center"><FontAwesomeIcon icon={faChevronLeft} className="mr-4"></FontAwesomeIcon> Back to Blog</Link>
<article className="container text-white mx-auto rounded-lg mt-4">
<header className="relative">
<div className="absolute h-full w-full flex items-center justify-center p-8">
<div className="bg-white text-brown bg-opacity-75 rounded p-4 md:p-12">
<h1 className="mono text-3xl mb-4">
{post.frontmatter.title}
</h1>
<div className="flex justify-center text-brown">
<img src={post.frontmatter.authorImage} alt="" className="w-10 h-10 rounded-full"/>
<p className="mono flex items-center pl-4 text-xl">{post.frontmatter.author}</p>
</div>
<p className="text-base font-semibold mt-4 text-center">Published on <strong>{post.frontmatter.date}</strong></p>
</div>
</div>
<img src={post.frontmatter.image} alt={post.frontmatter.title} className="w-full object-cover object-left-top rounded-t" style={{ height: "400px", width: "100%"}}/>
</header>
<div className="break-words px-4 lg:px-16 py-12 lg:py-20 prose lg:prose-xl max-w-screen leading-normal">
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</div>
</article>
</main>
</Layout>
)
}
export const postQuery = graphql`
query BlogPostByPath($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path } }){
html
frontmatter {
path
title
date
author
authorImage
image
}
}
}
`
When using the graphiql playground, the proper image path is retrieved for blog.js but not for blog-post.js (individual posts):
blog.js query:
{
"data": {
"allMarkdownRemark": {
"edges": [
{
"node": {
"frontmatter": {
"path": "/customizing-the-scrollbar-with-css",
"title": "Customizing the Scrollbar with CSS",
"description": "Learn how to apply custom styling to the scrollbar on your project with pure CSS",
"date": "2021-01-28",
"category": "CSS",
"image": "../images/scrollbar.png"
}
}
},
blog-post.js query:
{
"errors": [
{
"message": "Variable "$path" of required type "String!" was not provided.",
"locations": [
{
"line": 1,
"column": 24
}
],
"stack": [
"GraphQLError: Variable "$path" of required type "String!" was not provided.",
" at _loop (C:\Users\npeel\Microsoft VS Code\gatsby-blog-main\gatsby-blog-main\node_modules\graphql\execution\values.js:92:17)",
" at coerceVariableValues (C:\Users\npeel\Microsoft VS Code\gatsby-blog-main\gatsby-blog-main\node_modules\graphql\execution\values.js:119:16)",
" at getVariableValues (C:\Users\npeel\Microsoft VS Code\gatsby-blog-main\gatsby-blog-main\node_modules\graphql\execution\values.js:48:19)",
" at buildExecutionContext (C:\Users\npeel\Microsoft VS Code\gatsby-blog-main\gatsby-blog-main\node_modules\graphql\execution\execute.js:184:61)",
" at executeImpl (C:\Users\npeel\Microsoft VS Code\gatsby-blog-main\gatsby-blog-main\node_modules\graphql\execution\execute.js:89:20)",
" at execute (C:\Users\npeel\Microsoft VS Code\gatsby-blog-main\gatsby-blog-main\node_modules\graphql\execution\execute.js:64:35)",
" at C:\Users\npeel\Microsoft VS Code\gatsby-blog-main\gatsby-blog-main\node_modules\express-graphql\index.js:152:16",
" at runMicrotasks (<anonymous>)",
" at processTicksAndRejections (internal/process/task_queues.js:93:5)"
]
}
],
"extensions": {}
}
gatsby-node.js –
I received an error when adding ‘image’ and ‘authorImage’ to frontmatter here
const path = require('path');
exports.createPages = ({actions, graphql}) => {
const { createPage } = actions
const postTemplate = path.resolve('src/templates/blog-post.js');
return graphql(`
{
allMarkdownRemark{
edges {
node {
html
id
frontmatter {
path
title
date
author
category
description
}
}
}
}
}
`).then(res => {
if(res.errors) {
return Promise.reject(res.errors)
}
res.data.allMarkdownRemark.edges.forEach(({node}) => {
createPage({
path: node.frontmatter.path,
component: postTemplate
})
})
})
}
// for RSS feed:
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}
gatsby-config.js –
module.exports = {
siteMetadata: {
title: `Nikki Peel - Blog`,
description: `Technical blog created with Gatsby and Markdown`,
author: `Nikki Peel`,
siteUrl: `https://nikkipeel.netlify.app`,
},
plugins: [
`gatsby-transformer-remark`,
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
`gatsby-plugin-react-helmet`,
`gatsby-plugin-catch-links`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/src/pages`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `gatsby-starter-default`,
short_name: `starter`,
start_url: `/`,
background_color: `#663399`,
theme_color: `#663399`,
display: `minimal-ui`,
icon: `src/images/blog-logo.png`, // This path is relative to the root of the site.
},
},
`gatsby-plugin-feed`,
`gatsby-plugin-postcss`,
`gatsby-plugin-fontawesome-css`,
// this (optional) plugin enables Progressive Web App + Offline functionality
// To learn more, visit: https://gatsby.dev/offline
// `gatsby-plugin-offline`,
],
}
2
Answers
I was able to get the images to load by referencing an external source in the markdown files but still can't access them by file path 🤷♀️ I'm still researching and working on this issue
As you can see in the documentation, the when dealing with markdown plus images, the paths should be relative to the markdown file itself so:
From post-one: https://github.com/nikkipeel/gatsby-blog/blob/main/src/pages/post-one/index.md
Should become:
Note the relativity of both images (
../../images/selfie.jpg
). This will allow Gatsby to create a valid GraphQL node and it will display thechildImageSharp
node in the GraphQL playground (localhost:8000/___graphql
).Unlike it may seem, this is a good error because it’s pointing that Gatsby is trying to create nodes from your markdown files (i.e: your paths are well referenced now) and you will be able to use
gatsby-image
. I’ve realized that you are missing some plugins, according to the docs.Add the
gatsby-transformer-remark
and thegatsby-remark-images
plugin.I wouldn’t put the markdown files in the
/pages
folder by Gatsby to avoid potential issues. Gatsby is a folder-structure based routing, or in other words, Gatsby infers the internal structure of the/pages
folder to create the routing and the paths so it’s better to avoid mixing stuff.Your pages can be stored inside any folder if you set the
gatsby-source-filesystem
accordingly. This will allow Gatsby to create GraphQL nodes from those markdown files.