Gatsby: Gatsby Image with Netlify CMS - 'childImageSharp' of null

Created on 16 Mar 2019  ·  15Comments  ·  Source: gatsbyjs/gatsby

Hey, so I'm trying to get images working for blog posts for Netlify CMS. Had things working fine using regular MD with the gatsby-starter-blog but can't get it working with Netlify CMS. I think it may be something with the relative image path since it's showing up as null when it worked before but not sure. Have looked through blog posts and past issues but can't figure it out. Probably have been looking at it too long so any help would be much appreciated!

Here's the error: https://imgur.com/jhMZATJ. And here's what I have:

blog-post.js

import React from 'react'
import Img from 'gatsby-image'
import Helmet from 'react-helmet'
import { Link, graphql } from 'gatsby'
import get from 'lodash/get'

import Layout from '../components/layout'

class BlogPostTemplate extends React.Component {
  render() {
    const post = this.props.data.markdownRemark
    const siteTitle = get(this.props, 'data.site.siteMetadata.title')
    const siteDescription = post.excerpt
    const { previous, next } = this.props.pageContext

    return (
      <Layout location={this.props.location}>
        <Helmet
          htmlAttributes={{ lang: 'en' }}
          meta={[{ name: 'description', content: siteDescription }]}
          title={`${post.frontmatter.title} | ${siteTitle}`}
        />
        <Img className="mainImage" sizes={post.frontmatter.featuredImage.childImageSharp.sizes} />
        <div className="site-body">
          <div className="grid grid-gutters grid-justifyCenter">
            <div className="grid-cell">               
              <div className="blogTitle singleBlog">          
                <h1>{post.frontmatter.title}</h1>
                <h6>{post.frontmatter.date}</h6>
              </div>
              <div className="blogContent" dangerouslySetInnerHTML={{ __html: post.html }} />
              <hr />

              <ul
                style={{
                  display: 'flex',
                  flexWrap: 'wrap',
                  justifyContent: 'space-between',
                  listStyle: 'none',
                  padding: 0,
                }}
              >
                <li>
                  {previous && (
                    <Link to={previous.fields.slug} rel="prev">
                      ← {previous.frontmatter.title}
                    </Link>
                  )}
                </li>
                <li>
                  {next && (
                    <Link to={next.fields.slug} rel="next">
                      {next.frontmatter.title} →
                    </Link>
                  )}
                </li>
              </ul>
            </div>
          </div>
        </div>
      </Layout>
    )
  }
}

export default BlogPostTemplate

export const pageQuery = graphql`
  query BlogPostBySlug($slug: String!) {
    site {
      siteMetadata {
        title
        author
      }
    }
    markdownRemark(fields: { slug: { eq: $slug } }) {
      id
      excerpt
      html
      frontmatter {
        title
        date(formatString: "MMMM DD, YYYY")
        featuredImage {
          childImageSharp{
              sizes(maxWidth: 1500) {
                  ...GatsbyImageSharpSizes
              }
          }
        }
      }
    }
  }
`

gatsby-node.js

const _ = require('lodash')
const Promise = require('bluebird')
const path = require('path')
const { createFilePath } = require('gatsby-source-filesystem')

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions

  return new Promise((resolve, reject) => {
    const blogPost = path.resolve('src/templates/blog-post.js')
    resolve(
      graphql(
        `
          {
            allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }, limit: 1000) {
              edges {
                node {
                  fields {
                    slug
                  }
                  frontmatter {
                    title
                  }
                }
              }
            }
          }
        `
      ).then(result => {
        if (result.errors) {
          console.log(result.errors)
          reject(result.errors)
        }

        // Create blog posts pages.
        const posts = result.data.allMarkdownRemark.edges;

        _.each(posts, (post, index) => {
          const previous = index === posts.length - 1 ? null : posts[index + 1].node;
          const next = index === 0 ? null : posts[index - 1].node;

          createPage({
            path: post.node.fields.slug,
            component: blogPost,
            context: {
              slug: post.node.fields.slug,
              previous,
              next,
            },
          })
        })
      })
    )
  })
}

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions
  const { frontmatter } = node
  if (frontmatter) {
    const { image } = frontmatter
    if (image) {
      if (image.indexOf('/assets') === 0) {
        frontmatter.image = path.relative(
          path.dirname(node.fileAbsolutePath),
          path.join(__dirname, '/static/', image)
        )
      }
    }
  }

  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({ node, getNode })
    createNodeField({
      name: `slug`,
      node,
      value,
    })
  }
}

gatsby-config.js

module.exports = {
  siteMetadata: {
    title: 'Test Site',
    author: '',
    description: '',
    siteUrl: '',
  },
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/pages`,
        name: 'pages',
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/assets`,
        name: 'images',
      },
    },
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          {
            resolve: `gatsby-plugin-netlify-cms-paths`,
            options: {
            // Path to your Netlify CMS config file
            cmsConfig: `/static/admin/config.yml`
          }
          },
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 1500,
            },
          },
          {
            resolve: `gatsby-remark-responsive-iframe`,
            options: {
              wrapperStyle: `margin-bottom: 1.0725rem`,
            },
          },
          'gatsby-remark-prismjs',
          'gatsby-remark-copy-linked-files',
          'gatsby-remark-smartypants',
        ],
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        //trackingId: `ADD YOUR TRACKING ID HERE`,
      },
    },
    `gatsby-plugin-feed`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: ``,
        short_name: ``,
        start_url: `/`,
        background_color: `#ffffff`,
        theme_color: `#141414`,
        display: `minimal-ui`,
        icon: `src/assets/icon.png`,
      },
    },
    `gatsby-plugin-offline`,
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-netlify-cms`,
    `gatsby-plugin-netlify-cms-paths`,
    {
      resolve: `gatsby-plugin-postcss`,
      options: {
        postCssPlugins: [require(`postcss-custom-properties`)({ stage: 0 })],
      },
    },
    {
      resolve: `gatsby-plugin-favicon`,
      options: {
        logo: "./src/assets/icon.png",

        // WebApp Manifest Configuration
        appName: null, // Inferred with your package.json
        appDescription: null,
        developerName: null,
        developerURL: null,
        dir: 'auto',
        lang: 'en-US',
        background: '#fff',
        theme_color: '#fff',
        display: 'standalone',
        orientation: 'any',
        start_url: '/',
        version: '1.0',

        icons: {
          android: true,
          appleIcon: true,
          appleStartup: true,
          coast: false,
          favicons: true,
          firefox: true,
          opengraph: true,
          twitter: true,
          yandex: false,
          windows: false
        }
      }
    }
  ],
}

Netlify - config.yml

backend:
  name: github
  repo: jonathancary/test-site

media_folder: src/assets
public_folder: static

collections:
  - name: posts
    label: Posts
    folder: "src/pages/posts"
    create: true
    fields:
      - { name: title, label: Title }
      - { name: path, label: Path }
      - { name: date, label: Date, widget: date }
      - { name: description, label: Description }
      - { name: "tags", label: "Tags", widget: "list"}
      - { name: "featuredImage", label: "Featured Image", widget: "image" }
      - { name: body, label: Body, widget: markdown }
awaiting author response needs reproduction

Most helpful comment

@jonathancary i've picked up on your answer and i think i have a solution for your issue.
Going to break down my answer in smaller parts for a better understanding.

  • Installed the Gatsby blog starter like you mentioned.
  • Followed the official documentation for adding netlify cms, here, adding the packages in question.
  • Trimmed some stuff from the starter was it was not needed, namelly removed the bio component, the manifest plugin and the assets folder inside content. Adjusted accordingly in gatsby-config.js.
  • Replicated your static/admin/config.yml to my needs transforming it into
backend:
  name: github
  #name: test-repo
  repo: jonniebigodes/MYREPO

media_folder: static/assets
public_folder: assets

collections:
  - name: posts
    label: Posts
    #folder: "src/pages/posts"
    folder: /content/posts
    create: true
    fields:
      - { name: title, label: Title }
      - { name: path, label: Path }
      - { name: date, label: Date, widget: date }
      - { name: description, label: Description }
      - { name: "tags", label: "Tags", widget: "list"}
      - { name: "featuredImage", label: "Featured Image", widget: "image" }
      - { name: body, label: Body, widget: markdown }
  • Made some adjustments to the already present blog posts that come with the starter to reflect the your setup. So for instance the file content/posts/hello-world/index.md now has the following frontmatter keys inside:
title: Hello World
date: "2015-05-01T22:12:03.284Z"
featuredImage: golem.jpg
tags: ["one", "zero", "one"]
path: "/posts/my-first-post"

The image was copied over a old wallpaper folder i had laying around.

  • Modified gatsby-node.js so that the changes would be reflected.
exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions

  const blogPost = path.resolve(`./src/templates/blog-post.js`)
  return graphql(
    `
      {
        allMarkdownRemark(
          sort: { fields: [frontmatter___date], order: DESC }
          limit: 1000
        ) {
          edges {
            node {
              id
              fields {
                slug
              }
              frontmatter {
                title
                path
              }
            }
          }
        }
      }
    `
  ).then(result => {
    if (result.errors) {
      throw result.errors
    }

    // Create blog posts pages.
    const posts = result.data.allMarkdownRemark.edges

    posts.forEach((post, index) => {
      const previous = index === posts.length - 1 ? null : posts[index + 1].node
      const next = index === 0 ? null : posts[index - 1].node 


      createPage({
        //path: post.node.frontmatter.path,
        path: post.node.frontmatter.path,
        component: blogPost,
        context: {
          slug: post.node.fields.slug,
           previous,
          next,
        },
      })
    })

    return null
  })
}
  • Changed src/templates/blog-post to match yours, turning it into:
import React from "react"
import { Link, graphql } from "gatsby"
import Img from "gatsby-image"
import Layout from "../components/layout"
import SEO from "../components/seo"

class BlogPostTemplate extends React.Component {
  render() {
    const post = this.props.data.markdownRemark
    const siteTitle = this.props.data.site.siteMetadata.title

    const { previous, next } = this.props.pageContext

    return (
      <Layout location={this.props.location} title={siteTitle}>
        <SEO
          title={post.frontmatter.title}
          description={post.frontmatter.description || post.excerpt}
        />
        <Img
          className="mainImage"
          fluid={post.frontmatter.featuredImage.childImageSharp.fluid}
        />
        <div className="site-body">
          <div className="grid grid-gutters grid-justifyCenter">
            <div className="grid-cell">
              <div className="blogTitle singleBlog">
                <h1>{post.frontmatter.title}</h1>
                <h6>{post.frontmatter.date}</h6>
              </div>
              <div
                className="blogContent"
                dangerouslySetInnerHTML={{ __html: post.html }}
              />
              <hr />
               <ul
                style={{
                  display: `flex`,
                  flexWrap: `wrap`,
                  justifyContent: `space-between`,
                  listStyle: `none`,
                  padding: 0,
                }}
              >
                <li>
                  {previous && (
                    <Link to={previous.fields.slug} rel="prev">
                      ← {previous.frontmatter.title}
                    </Link>
                  )}
                </li>
                <li>
                  {next && (
                    <Link to={next.fields.slug} rel="next">
                      {next.frontmatter.title} →
                    </Link>
                  )}
                </li>
              </ul>
            </div>
          </div>
        </div>
      </Layout>
    )
  }
}

export default BlogPostTemplate

export const pageQuery = graphql`
  query BlogPostBySlug($slug: String!) {
    site {
      siteMetadata {
        title
        author
      }
    }
    markdownRemark(fields: { slug: { eq: $slug } }) {
      id
      excerpt(pruneLength: 160)
      html
      frontmatter {
        title
        date(formatString: "MMMM DD,YYYY")
        featuredImage {
          childImageSharp {
            fluid(maxWidth: 1500) {
              ...GatsbyImageSharpFluid_noBase64
            }
          }
        }
      }
    }
  }
`

Now i would like to point out a couple of things before continuing.

  1. The graphl query you have mentioned initially, namelly:
query BlogPostBySlug($slug: String!) {
    site {
      siteMetadata {
        title
        author
      }
    }
    markdownRemark(fields: { slug: { eq: $slug } }) {
      id
      excerpt
      html
      frontmatter {
        title
        date(formatString: "MMMM DD, YYYY")
        featuredImage {
          childImageSharp{
              sizes(maxWidth: 1500) { #<= this is deprecated
                  ...GatsbyImageSharpSizes
              }
          }
        }
      }
    }
  }

The sizes part is now deprecated as of the present version of gatsby and the corresponding plugin. As you can see below;
test_jonathan_cary_deprecated_image_warning

  1. The code you added in the comment above, more specifically in item number 5, will not work, in my end React would throw errors left and right when i visited a page with that code. Starting at the following. My reading on this is that for reasons unknown to me you're injecting a property unknown to the div element.
<div location={this.props.location}>..</div>

With the above out of the way.

  • Issuing gatsby develop, i would get a successfull build, opening up http://localhost:8000/ would show me the following:
    test_jonathan_cary_local_build_ok
    And opening a random post would show me the following:
    test_jonathan_cary_local_post
  • I thought. working locally, time to push this to netlify. Followed the steps to add this reproduction to netlify and use netlify cms. Everything went fine.
  • Created a new post in netlify cms backend and waited for netlify to detect the changes and start the build process. And i was greated with a build error with the following message:
5:24:40 PM: error GraphQL Error Field "featuredImage" must not have a selection since type "String" has no subfields.
5:24:40 PM:   file: /opt/build/repo/src/templates/blog-post.js
5:24:40 PM:    6 |         author
5:24:40 PM:    7 |       }
5:24:40 PM:    8 |     }
5:24:40 PM:    9 |     markdownRemark(fields: { slug: { eq: $slug } }) {
5:24:40 PM:   10 |       id
5:24:40 PM:   11 |       excerpt(pruneLength: 160)
5:24:40 PM:   12 |       html
5:24:40 PM:   13 |       frontmatter {
5:24:40 PM:   14 |         title
5:24:40 PM:   15 |         date(formatString: "MMMM DD,YYYY")
5:24:40 PM: > 16 |         featuredImage {
5:24:40 PM:      |                       ^
5:24:40 PM:   17 |           childImageSharp {
5:24:40 PM:   18 |             fluid(maxWidth: 1500) {
5:24:40 PM:   19 |               ...GatsbyImageSharpFluid_noBase64
5:24:40 PM:   20 |             }
5:24:40 PM:   21 |           }
5:24:40 PM:   22 |         }
5:24:40 PM:   23 |       }
5:24:40 PM:   24 |     }
5:24:41 PM: failed during stage 'building site': Build script returned non-zero exit code: 1
5:24:40 PM:   25 |   }

I know from experience that when both the markdown and a image file are "siblings", are under the same folder when the graphql query is executed, in this case the field featuredImage is treated as a File node, instead of a string, that happens if for instance the image is one level above. Leading to this.

  • I started digging and researching, reading all kinds of articles, read the and tested the official example from netlify, tweaked it a bit and same result as always.
  • Then it hit me, i was reading the documentation for gatsby-remark-relative-images on npm, to see if i was missing anything. And there, under the FAQ, was the answer.
    I changed the gatsby-config.js to the following:
module.exports = {
  siteMetadata: {
    title: `test_netlify_cms_image`,
    author: `jonniebigodes`,
    description: `Example code for netlify cms and images`,
    siteUrl: `https://gatsby-starter-blog-demo.netlify.com/`,
  },
  plugins: [
    {
      resolve:`gatsby-source-filesystem`, // this entry has to be the first or will not work as per FAQ 
      options:{
        path:`${__dirname}/static/assets`,
        name:`assets`
      }
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/posts`,
        name: `posts`,
      },
    },
   /*  {
      resolve: `gatsby-plugin-netlify-cms-paths`,
      options: {
        // Path to your Netlify CMS config file
        cmsConfig: `/static/admin/config.yml`
      }
    }, */
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          `gatsby-remark-relative-images`,
          /* {
            resolve: `gatsby-plugin-netlify-cms-paths`,
            options: {
              cmsConfig: `/static/admin/config.yml`,
            },
          }, */
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 1500,
            },
          },
          {
            resolve: `gatsby-remark-responsive-iframe`,
            options: {
              wrapperStyle: `margin-bottom: 1.0725rem`,
            },
          },

          `gatsby-remark-prismjs`,
          {
            resolve:`gatsby-remark-copy-linked-files`,
            options:{
              destinationDir:`${__dirname}/static`
            }
          },
          {
            resolve:`gatsby-remark-copy-linked-files`,
            options:{
              destinationDir:`${__dirname}/static`
            }
          },
          `gatsby-remark-smartypants`,
        ],
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        //trackingId: `ADD YOUR TRACKING ID HERE`,
      },
    },
    `gatsby-plugin-feed`,
    /* `gatsby-plugin-offline`, */
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-plugin-typography`,
      options: {
        pathToConfigModule: `src/utils/typography`,
      },
    },
    `gatsby-plugin-netlify-cms`,
    `gatsby-plugin-netlify` 
  ],
}

  • Changed gatsby-node.js to the following:
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
const {fmImagesToRelative} = require('gatsby-remark-relative-images');
exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions

  const blogPost = path.resolve(`./src/templates/blog-post.js`)
  return graphql(
    `
      {
        allMarkdownRemark(
          sort: { fields: [frontmatter___date], order: DESC }
          limit: 1000
        ) {
          edges {
            node {
              id
              fields {
                slug
              }
              frontmatter {
                title
                path
              }
            }
          }
        }
      }
    `
  ).then(result => {
    if (result.errors) {
      throw result.errors
    }

    // Create blog posts pages.
    const posts = result.data.allMarkdownRemark.edges

    posts.forEach((post, index) => {
      // paging will not work as is, throws a build error  
      /* const previous = index === posts.length - 1 ? null : posts[index + 1].node
      const next = index === 0 ? null : posts[index - 1].node */


      createPage({
        //path: post.node.frontmatter.path,
         // changed to this as the first item path key  processed would throw a empty string 
        path: post.node.frontmatter.path===""?`/posts/${post.node.fields.slug}`:post.node.frontmatter.path,
        component: blogPost,
        context: {
          slug: post.node.fields.slug,
          /* previous,
          next, */
        },
      })
    })

    return null
  })
}

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions
  fmImagesToRelative(node) 
  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({ node, getNode })
    createNodeField({
      name: `slug`,
      node,
      value,
    })
  }
}

  • Modified the template src/template\blog-post to the following:
import React from "react"
import { graphql } from "gatsby"
import Img from "gatsby-image"
import Layout from "../components/layout"
import SEO from "../components/seo"

class BlogPostTemplate extends React.Component {
  render() {
    const post = this.props.data.markdownRemark
    const siteTitle = this.props.data.site.siteMetadata.title
    return (
      <Layout location={this.props.location} title={siteTitle}>
        <SEO
          title={post.frontmatter.title}
          description={post.frontmatter.description || post.excerpt}
        />
        <Img
          className="mainImage"
          fluid={post.frontmatter.featuredImage.childImageSharp.fluid}
        />
        <div className="site-body">
          <div className="grid grid-gutters grid-justifyCenter">
            <div className="grid-cell">
              <div className="blogTitle singleBlog">
                <h1>{post.frontmatter.title}</h1>
                <h6>{post.frontmatter.date}</h6>
              </div>
              <div
                className="blogContent"
                dangerouslySetInnerHTML={{ __html: post.html }}
              />
              <hr />
            </div>
          </div>
        </div>
      </Layout>
    )
  }
}

export default BlogPostTemplate

export const pageQuery = graphql`
  query BlogPostBySlug($slug: String!) {
    site {
      siteMetadata {
        title
        author
      }
    }
    markdownRemark(fields: { slug: { eq: $slug } }) {
      id
      excerpt(pruneLength: 160)
      html
      frontmatter {
        title
        date(formatString: "MMMM DD,YYYY")
        featuredImage {
          childImageSharp {
            fluid(maxWidth: 1500) {
              ...GatsbyImageSharpFluid_noBase64
            }
          }
        }
      }
    }
  }
`
  • And finally src/pages/index.js to the following:
import React from "react"
import { Link, graphql } from "gatsby"
import Layout from "../components/layout"
import SEO from "../components/seo"
import { rhythm } from "../utils/typography"

class BlogIndex extends React.Component {
  render() {
    const { data } = this.props
    const siteTitle = data.site.siteMetadata.title
    const posts = data.allMarkdownRemark.edges

    return (
      <Layout location={this.props.location} title={siteTitle}>
        <SEO
          title="All posts"
          keywords={[`blog`, `gatsby`, `javascript`, `react`]}
        />
        {posts.map(({ node }) => {
          const title = node.frontmatter.title || node.fields.slug
          return (
            <div key={node.fields.slug}>
              <h3
                style={{
                  marginBottom: rhythm(1 / 4),
                }}
              >
                <Link style={{ boxShadow: `none` }} to={node.frontmatter.path}>
                  {title}
                </Link>
              </h3>
              <small>{node.frontmatter.date}</small>
              <p
                dangerouslySetInnerHTML={{
                  __html: node.frontmatter.description || node.excerpt,
                }}
              />
            </div>
          )
        })}
      </Layout>
    )
  }
}

export default BlogIndex

export const pageQuery = graphql`
  query {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      edges {
        node {
          excerpt
          fields {
            slug
          }
          frontmatter {
            date(formatString: "MMMM DD, YYYY")
            title
            description
            path
          }
        }
      }
    }
  }
`
  • Committed it to the repository, waited for netlify to pick up the changes and i'm presented with a successfull build.
  • To be sure, i went on to netlify cms and created a new post with the data as you can see below:
    test_jonathan_cary_post_netlify1
    and
    test_jonathan_cary_post_netlify2

  • Waited for netlify to pick up on the changes and build the site once more. It went without a problem and my post was added as you can see
    test_jonathan_cary_ok

Before i end the comment. A couple of things.

  • Pagination will not work with the changes mentioned here. Gatsby will throw a error and prevent the build, either in development mode or production mode.
  • In this case as i've maintained the existing blog posts if you don't make the adjustment in gatsby node under create page, Gatsby will throw another error saying something like a empty string cannot be a page. From testing it looks like even if you have explicitly define a frontmatter key with a path, the first item iterated will always be a empty string. Probably additional configuration is needed, but i was not willing to do it, i wanted to have the content to injected directly from netlify cms, so that's the reason for that little conditional in createPage.

Sorry for the extremely long comment. But it was best this way so that every step was documented in order for you to follow it and make the adjustments to your needs.
Hope it will solve your issue.
Feel free to provide feedback

All 15 comments

It's probably caused by the relative path of the images.
Instead of this:

  const { frontmatter } = node
  if (frontmatter) {
    const { image } = frontmatter
    if (image) {
      if (image.indexOf('/assets') === 0) {
        frontmatter.image = path.relative(
          path.dirname(node.fileAbsolutePath),
          path.join(__dirname, '/static/', image)
        )
      }
    }
  }

try this.

Tried that but still getting the same error. This is what the update gatsby-node.js file looks like:

const _ = require('lodash')
const Promise = require('bluebird')
const path = require('path')
const { createFilePath } = require('gatsby-source-filesystem')
const { fmImagesToRelative } = require('gatsby-remark-relative-images')

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions

  return new Promise((resolve, reject) => {
    const blogPost = path.resolve('src/templates/blog-post.js')
    resolve(
      graphql(
        `
          {
            allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }, limit: 1000) {
              edges {
                node {
                  fields {
                    slug
                  }
                  frontmatter {
                    title
                  }
                }
              }
            }
          }
        `
      ).then(result => {
        if (result.errors) {
          console.log(result.errors)
          reject(result.errors)
        }

        // Create blog posts pages.
        const posts = result.data.allMarkdownRemark.edges;

        _.each(posts, (post, index) => {
          const previous = index === posts.length - 1 ? null : posts[index + 1].node;
          const next = index === 0 ? null : posts[index - 1].node;

          createPage({
            path: post.node.fields.slug,
            component: blogPost,
            context: {
              slug: post.node.fields.slug,
              previous,
              next,
            },
          })
        })
      })
    )
  })
}

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions
  fmImagesToRelative(node) // convert image paths for gatsby images

  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({ node, getNode })
    createNodeField({
      name: `slug`,
      node,
      value,
    })
  }
}

In my experience, the reason is that your featuredImage file are not recognized as an image File type by build process, just a normal string. So, in order to solve this, you must add the file path into source system, or check the featuredImage value, if it is right and in the assets directory.

If you had used images in assets directory, and image path is also right, then modify the gatsby-transformer-remark plugins definition in gatsby-config.js, my config is:

    {
      resolve: 'gatsby-transformer-remark',
      options: {
        "excerpt_separator": `<!-- end -->`,
        plugins: [
          {
            resolve: 'gatsby-remark-relative-images',
            options: {
              name: 'uploads',
            },
          },
          {
            resolve: 'gatsby-remark-images',
            options: {
              // It's important to specify the maxWidth (in pixels) of
              // the content container as this plugin uses this as the
              // base for generating different widths of each image.
              maxWidth: 1280,
            },
          },
          {
            resolve: 'gatsby-remark-copy-linked-files',
            options: {
              destinationDir: 'static',
            }
          }
        ]
      }
    },

Still didn't work, this is the build error I keep getting from Netlify after I try adding a blog post and it rebuilds:

10:50:30 PM: info bootstrap finished - 26.475 s
10:50:45 PM: success Building production JavaScript and CSS bundles — 14.255 s
10:51:09 PM: error Building static HTML failed for path "/posts/testing/"
10:51:09 PM: See our docs page on debugging HTML builds for help https://gatsby.dev/debug-html
10:51:09 PM:   21 |           title={`${post.frontmatter.title} | ${siteTitle}`}
10:51:09 PM:   22 |         />
10:51:09 PM: > 23 |         <Img className="mainImage" sizes={post.frontmatter.featuredImage.childImageSharp.sizes} />
10:51:09 PM:      |                                                                          ^
10:51:09 PM:   24 |         <div className="site-body">
10:51:09 PM:   25 |           <div className="grid grid-gutters grid-justifyCenter">
10:51:09 PM:   26 |             <div className="grid-cell">
10:51:09 PM: 
10:51:09 PM:   WebpackError: TypeError: Cannot read property 'childImageSharp' of null

@jonathancary can you please share a reproducible example we can have a look at?

Not sure if you were wanting me to create a public repo for it or just looking for the instructions to reproduce. Here are the steps. Let me know if you need any further details. Thanks!

  1. Install the Gatsby Blog Starter:
    gatsby new gatsby-blog https://github.com/gatsbyjs/gatsby-starter-blog

  2. Add Netlify CMS: npm install --save netlify-cms gatsby-plugin-netlify-cms

  3. Add it to gatsby-config.js

resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          {
            resolve: `gatsby-plugin-netlify-cms-paths`,
            options: {
            // Path to your Netlify CMS config file
            cmsConfig: `/static/admin/config.yml`
            }
          },
  1. Change Netlify config.yml to look like the following (Trying to have images stored in the same assets folder as the other ones:
backend:
  name: github
  repo: jonathancary/YOURREPO

media_folder: src/assets
public_folder: static

collections:
  - name: posts
    label: Posts
    folder: "src/pages/posts"
    create: true
    fields:
      - { name: title, label: Title }
      - { name: path, label: Path }
      - { name: date, label: Date, widget: date }
      - { name: description, label: Description }
      - { name: "tags", label: "Tags", widget: "list"}
      - { name: "featuredImage", label: "Featured Image", widget: "image" }
      - { name: body, label: Body, widget: markdown }
  1. Change templates/blog-post.js to look like the following:
import React from 'react'
import Img from 'gatsby-image'
import Helmet from 'react-helmet'
import { Link, graphql } from 'gatsby'
import get from 'lodash/get'

class BlogPostTemplate extends React.Component {
  render() {
    const post = this.props.data.markdownRemark
    const siteTitle = get(this.props, 'data.site.siteMetadata.title')
    const siteDescription = post.excerpt
    const { previous, next } = this.props.pageContext

    return (
      <div location={this.props.location}>
        <Helmet
          htmlAttributes={{ lang: 'en' }}
          meta={[{ name: 'description', content: siteDescription }]}
          title={`${post.frontmatter.title} | ${siteTitle}`}
        />
        <Img className="mainImage" sizes={post.frontmatter.featuredImage.childImageSharp.sizes} />
        <div className="site-body">
          <div className="grid grid-gutters grid-justifyCenter">
            <div className="grid-cell">               
              <div className="blogTitle singleBlog">          
                <h1>{post.frontmatter.title}</h1>
                <h6>{post.frontmatter.date}</h6>
              </div>
              <div className="blogContent" dangerouslySetInnerHTML={{ __html: post.html }} />
              <hr />

              <ul
                style={{
                  display: 'flex',
                  flexWrap: 'wrap',
                  justifyContent: 'space-between',
                  listStyle: 'none',
                  padding: 0,
                }}
              >
                <li>
                  {previous && (
                    <Link to={previous.fields.slug} rel="prev">
                      ← {previous.frontmatter.title}
                    </Link>
                  )}
                </li>
                <li>
                  {next && (
                    <Link to={next.fields.slug} rel="next">
                      {next.frontmatter.title} →
                    </Link>
                  )}
                </li>
              </ul>
            </div>
          </div>
        </div>
      </div>
    )
  }
}

export default BlogPostTemplate

export const pageQuery = graphql`
  query BlogPostBySlug($slug: String!) {
    site {
      siteMetadata {
        title
        author
      }
    }
    markdownRemark(fields: { slug: { eq: $slug } }) {
      id
      excerpt
      html
      frontmatter {
        title
        date(formatString: "MMMM DD, YYYY")
        featuredImage {
          childImageSharp{
              sizes(maxWidth: 1500) {
                  ...GatsbyImageSharpSizes
              }
          }
        }
      }
    }
  }
`
  1. Commit to Github and Deploy to Netlify

  2. Go to yourwebsite.com/admin and login in to CMS. Add a blog post with either an uploaded featured image or choosing one already in the folder. (As you can see when you choose or upload an image it doesn't show correctly in the preview panel either.

  3. When Netlify rebuilds the site you'll get the same error and the build will fail.

@jonathancary i've picked up on your answer and i think i have a solution for your issue.
Going to break down my answer in smaller parts for a better understanding.

  • Installed the Gatsby blog starter like you mentioned.
  • Followed the official documentation for adding netlify cms, here, adding the packages in question.
  • Trimmed some stuff from the starter was it was not needed, namelly removed the bio component, the manifest plugin and the assets folder inside content. Adjusted accordingly in gatsby-config.js.
  • Replicated your static/admin/config.yml to my needs transforming it into
backend:
  name: github
  #name: test-repo
  repo: jonniebigodes/MYREPO

media_folder: static/assets
public_folder: assets

collections:
  - name: posts
    label: Posts
    #folder: "src/pages/posts"
    folder: /content/posts
    create: true
    fields:
      - { name: title, label: Title }
      - { name: path, label: Path }
      - { name: date, label: Date, widget: date }
      - { name: description, label: Description }
      - { name: "tags", label: "Tags", widget: "list"}
      - { name: "featuredImage", label: "Featured Image", widget: "image" }
      - { name: body, label: Body, widget: markdown }
  • Made some adjustments to the already present blog posts that come with the starter to reflect the your setup. So for instance the file content/posts/hello-world/index.md now has the following frontmatter keys inside:
title: Hello World
date: "2015-05-01T22:12:03.284Z"
featuredImage: golem.jpg
tags: ["one", "zero", "one"]
path: "/posts/my-first-post"

The image was copied over a old wallpaper folder i had laying around.

  • Modified gatsby-node.js so that the changes would be reflected.
exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions

  const blogPost = path.resolve(`./src/templates/blog-post.js`)
  return graphql(
    `
      {
        allMarkdownRemark(
          sort: { fields: [frontmatter___date], order: DESC }
          limit: 1000
        ) {
          edges {
            node {
              id
              fields {
                slug
              }
              frontmatter {
                title
                path
              }
            }
          }
        }
      }
    `
  ).then(result => {
    if (result.errors) {
      throw result.errors
    }

    // Create blog posts pages.
    const posts = result.data.allMarkdownRemark.edges

    posts.forEach((post, index) => {
      const previous = index === posts.length - 1 ? null : posts[index + 1].node
      const next = index === 0 ? null : posts[index - 1].node 


      createPage({
        //path: post.node.frontmatter.path,
        path: post.node.frontmatter.path,
        component: blogPost,
        context: {
          slug: post.node.fields.slug,
           previous,
          next,
        },
      })
    })

    return null
  })
}
  • Changed src/templates/blog-post to match yours, turning it into:
import React from "react"
import { Link, graphql } from "gatsby"
import Img from "gatsby-image"
import Layout from "../components/layout"
import SEO from "../components/seo"

class BlogPostTemplate extends React.Component {
  render() {
    const post = this.props.data.markdownRemark
    const siteTitle = this.props.data.site.siteMetadata.title

    const { previous, next } = this.props.pageContext

    return (
      <Layout location={this.props.location} title={siteTitle}>
        <SEO
          title={post.frontmatter.title}
          description={post.frontmatter.description || post.excerpt}
        />
        <Img
          className="mainImage"
          fluid={post.frontmatter.featuredImage.childImageSharp.fluid}
        />
        <div className="site-body">
          <div className="grid grid-gutters grid-justifyCenter">
            <div className="grid-cell">
              <div className="blogTitle singleBlog">
                <h1>{post.frontmatter.title}</h1>
                <h6>{post.frontmatter.date}</h6>
              </div>
              <div
                className="blogContent"
                dangerouslySetInnerHTML={{ __html: post.html }}
              />
              <hr />
               <ul
                style={{
                  display: `flex`,
                  flexWrap: `wrap`,
                  justifyContent: `space-between`,
                  listStyle: `none`,
                  padding: 0,
                }}
              >
                <li>
                  {previous && (
                    <Link to={previous.fields.slug} rel="prev">
                      ← {previous.frontmatter.title}
                    </Link>
                  )}
                </li>
                <li>
                  {next && (
                    <Link to={next.fields.slug} rel="next">
                      {next.frontmatter.title} →
                    </Link>
                  )}
                </li>
              </ul>
            </div>
          </div>
        </div>
      </Layout>
    )
  }
}

export default BlogPostTemplate

export const pageQuery = graphql`
  query BlogPostBySlug($slug: String!) {
    site {
      siteMetadata {
        title
        author
      }
    }
    markdownRemark(fields: { slug: { eq: $slug } }) {
      id
      excerpt(pruneLength: 160)
      html
      frontmatter {
        title
        date(formatString: "MMMM DD,YYYY")
        featuredImage {
          childImageSharp {
            fluid(maxWidth: 1500) {
              ...GatsbyImageSharpFluid_noBase64
            }
          }
        }
      }
    }
  }
`

Now i would like to point out a couple of things before continuing.

  1. The graphl query you have mentioned initially, namelly:
query BlogPostBySlug($slug: String!) {
    site {
      siteMetadata {
        title
        author
      }
    }
    markdownRemark(fields: { slug: { eq: $slug } }) {
      id
      excerpt
      html
      frontmatter {
        title
        date(formatString: "MMMM DD, YYYY")
        featuredImage {
          childImageSharp{
              sizes(maxWidth: 1500) { #<= this is deprecated
                  ...GatsbyImageSharpSizes
              }
          }
        }
      }
    }
  }

The sizes part is now deprecated as of the present version of gatsby and the corresponding plugin. As you can see below;
test_jonathan_cary_deprecated_image_warning

  1. The code you added in the comment above, more specifically in item number 5, will not work, in my end React would throw errors left and right when i visited a page with that code. Starting at the following. My reading on this is that for reasons unknown to me you're injecting a property unknown to the div element.
<div location={this.props.location}>..</div>

With the above out of the way.

  • Issuing gatsby develop, i would get a successfull build, opening up http://localhost:8000/ would show me the following:
    test_jonathan_cary_local_build_ok
    And opening a random post would show me the following:
    test_jonathan_cary_local_post
  • I thought. working locally, time to push this to netlify. Followed the steps to add this reproduction to netlify and use netlify cms. Everything went fine.
  • Created a new post in netlify cms backend and waited for netlify to detect the changes and start the build process. And i was greated with a build error with the following message:
5:24:40 PM: error GraphQL Error Field "featuredImage" must not have a selection since type "String" has no subfields.
5:24:40 PM:   file: /opt/build/repo/src/templates/blog-post.js
5:24:40 PM:    6 |         author
5:24:40 PM:    7 |       }
5:24:40 PM:    8 |     }
5:24:40 PM:    9 |     markdownRemark(fields: { slug: { eq: $slug } }) {
5:24:40 PM:   10 |       id
5:24:40 PM:   11 |       excerpt(pruneLength: 160)
5:24:40 PM:   12 |       html
5:24:40 PM:   13 |       frontmatter {
5:24:40 PM:   14 |         title
5:24:40 PM:   15 |         date(formatString: "MMMM DD,YYYY")
5:24:40 PM: > 16 |         featuredImage {
5:24:40 PM:      |                       ^
5:24:40 PM:   17 |           childImageSharp {
5:24:40 PM:   18 |             fluid(maxWidth: 1500) {
5:24:40 PM:   19 |               ...GatsbyImageSharpFluid_noBase64
5:24:40 PM:   20 |             }
5:24:40 PM:   21 |           }
5:24:40 PM:   22 |         }
5:24:40 PM:   23 |       }
5:24:40 PM:   24 |     }
5:24:41 PM: failed during stage 'building site': Build script returned non-zero exit code: 1
5:24:40 PM:   25 |   }

I know from experience that when both the markdown and a image file are "siblings", are under the same folder when the graphql query is executed, in this case the field featuredImage is treated as a File node, instead of a string, that happens if for instance the image is one level above. Leading to this.

  • I started digging and researching, reading all kinds of articles, read the and tested the official example from netlify, tweaked it a bit and same result as always.
  • Then it hit me, i was reading the documentation for gatsby-remark-relative-images on npm, to see if i was missing anything. And there, under the FAQ, was the answer.
    I changed the gatsby-config.js to the following:
module.exports = {
  siteMetadata: {
    title: `test_netlify_cms_image`,
    author: `jonniebigodes`,
    description: `Example code for netlify cms and images`,
    siteUrl: `https://gatsby-starter-blog-demo.netlify.com/`,
  },
  plugins: [
    {
      resolve:`gatsby-source-filesystem`, // this entry has to be the first or will not work as per FAQ 
      options:{
        path:`${__dirname}/static/assets`,
        name:`assets`
      }
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/content/posts`,
        name: `posts`,
      },
    },
   /*  {
      resolve: `gatsby-plugin-netlify-cms-paths`,
      options: {
        // Path to your Netlify CMS config file
        cmsConfig: `/static/admin/config.yml`
      }
    }, */
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          `gatsby-remark-relative-images`,
          /* {
            resolve: `gatsby-plugin-netlify-cms-paths`,
            options: {
              cmsConfig: `/static/admin/config.yml`,
            },
          }, */
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 1500,
            },
          },
          {
            resolve: `gatsby-remark-responsive-iframe`,
            options: {
              wrapperStyle: `margin-bottom: 1.0725rem`,
            },
          },

          `gatsby-remark-prismjs`,
          {
            resolve:`gatsby-remark-copy-linked-files`,
            options:{
              destinationDir:`${__dirname}/static`
            }
          },
          {
            resolve:`gatsby-remark-copy-linked-files`,
            options:{
              destinationDir:`${__dirname}/static`
            }
          },
          `gatsby-remark-smartypants`,
        ],
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        //trackingId: `ADD YOUR TRACKING ID HERE`,
      },
    },
    `gatsby-plugin-feed`,
    /* `gatsby-plugin-offline`, */
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-plugin-typography`,
      options: {
        pathToConfigModule: `src/utils/typography`,
      },
    },
    `gatsby-plugin-netlify-cms`,
    `gatsby-plugin-netlify` 
  ],
}

  • Changed gatsby-node.js to the following:
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
const {fmImagesToRelative} = require('gatsby-remark-relative-images');
exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions

  const blogPost = path.resolve(`./src/templates/blog-post.js`)
  return graphql(
    `
      {
        allMarkdownRemark(
          sort: { fields: [frontmatter___date], order: DESC }
          limit: 1000
        ) {
          edges {
            node {
              id
              fields {
                slug
              }
              frontmatter {
                title
                path
              }
            }
          }
        }
      }
    `
  ).then(result => {
    if (result.errors) {
      throw result.errors
    }

    // Create blog posts pages.
    const posts = result.data.allMarkdownRemark.edges

    posts.forEach((post, index) => {
      // paging will not work as is, throws a build error  
      /* const previous = index === posts.length - 1 ? null : posts[index + 1].node
      const next = index === 0 ? null : posts[index - 1].node */


      createPage({
        //path: post.node.frontmatter.path,
         // changed to this as the first item path key  processed would throw a empty string 
        path: post.node.frontmatter.path===""?`/posts/${post.node.fields.slug}`:post.node.frontmatter.path,
        component: blogPost,
        context: {
          slug: post.node.fields.slug,
          /* previous,
          next, */
        },
      })
    })

    return null
  })
}

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions
  fmImagesToRelative(node) 
  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({ node, getNode })
    createNodeField({
      name: `slug`,
      node,
      value,
    })
  }
}

  • Modified the template src/template\blog-post to the following:
import React from "react"
import { graphql } from "gatsby"
import Img from "gatsby-image"
import Layout from "../components/layout"
import SEO from "../components/seo"

class BlogPostTemplate extends React.Component {
  render() {
    const post = this.props.data.markdownRemark
    const siteTitle = this.props.data.site.siteMetadata.title
    return (
      <Layout location={this.props.location} title={siteTitle}>
        <SEO
          title={post.frontmatter.title}
          description={post.frontmatter.description || post.excerpt}
        />
        <Img
          className="mainImage"
          fluid={post.frontmatter.featuredImage.childImageSharp.fluid}
        />
        <div className="site-body">
          <div className="grid grid-gutters grid-justifyCenter">
            <div className="grid-cell">
              <div className="blogTitle singleBlog">
                <h1>{post.frontmatter.title}</h1>
                <h6>{post.frontmatter.date}</h6>
              </div>
              <div
                className="blogContent"
                dangerouslySetInnerHTML={{ __html: post.html }}
              />
              <hr />
            </div>
          </div>
        </div>
      </Layout>
    )
  }
}

export default BlogPostTemplate

export const pageQuery = graphql`
  query BlogPostBySlug($slug: String!) {
    site {
      siteMetadata {
        title
        author
      }
    }
    markdownRemark(fields: { slug: { eq: $slug } }) {
      id
      excerpt(pruneLength: 160)
      html
      frontmatter {
        title
        date(formatString: "MMMM DD,YYYY")
        featuredImage {
          childImageSharp {
            fluid(maxWidth: 1500) {
              ...GatsbyImageSharpFluid_noBase64
            }
          }
        }
      }
    }
  }
`
  • And finally src/pages/index.js to the following:
import React from "react"
import { Link, graphql } from "gatsby"
import Layout from "../components/layout"
import SEO from "../components/seo"
import { rhythm } from "../utils/typography"

class BlogIndex extends React.Component {
  render() {
    const { data } = this.props
    const siteTitle = data.site.siteMetadata.title
    const posts = data.allMarkdownRemark.edges

    return (
      <Layout location={this.props.location} title={siteTitle}>
        <SEO
          title="All posts"
          keywords={[`blog`, `gatsby`, `javascript`, `react`]}
        />
        {posts.map(({ node }) => {
          const title = node.frontmatter.title || node.fields.slug
          return (
            <div key={node.fields.slug}>
              <h3
                style={{
                  marginBottom: rhythm(1 / 4),
                }}
              >
                <Link style={{ boxShadow: `none` }} to={node.frontmatter.path}>
                  {title}
                </Link>
              </h3>
              <small>{node.frontmatter.date}</small>
              <p
                dangerouslySetInnerHTML={{
                  __html: node.frontmatter.description || node.excerpt,
                }}
              />
            </div>
          )
        })}
      </Layout>
    )
  }
}

export default BlogIndex

export const pageQuery = graphql`
  query {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      edges {
        node {
          excerpt
          fields {
            slug
          }
          frontmatter {
            date(formatString: "MMMM DD, YYYY")
            title
            description
            path
          }
        }
      }
    }
  }
`
  • Committed it to the repository, waited for netlify to pick up the changes and i'm presented with a successfull build.
  • To be sure, i went on to netlify cms and created a new post with the data as you can see below:
    test_jonathan_cary_post_netlify1
    and
    test_jonathan_cary_post_netlify2

  • Waited for netlify to pick up on the changes and build the site once more. It went without a problem and my post was added as you can see
    test_jonathan_cary_ok

Before i end the comment. A couple of things.

  • Pagination will not work with the changes mentioned here. Gatsby will throw a error and prevent the build, either in development mode or production mode.
  • In this case as i've maintained the existing blog posts if you don't make the adjustment in gatsby node under create page, Gatsby will throw another error saying something like a empty string cannot be a page. From testing it looks like even if you have explicitly define a frontmatter key with a path, the first item iterated will always be a empty string. Probably additional configuration is needed, but i was not willing to do it, i wanted to have the content to injected directly from netlify cms, so that's the reason for that little conditional in createPage.

Sorry for the extremely long comment. But it was best this way so that every step was documented in order for you to follow it and make the adjustments to your needs.
Hope it will solve your issue.
Feel free to provide feedback

Where is your netlify admin config.yml
Mine looks like this with images stored in static/img/
`backend:
name: git-gateway
repo: donaldboulton/publiuslogic
branch: master
identity_url: "https://publiuslogic.com/.netlify/identity"
gateway_url: "https://publiuslogic.com/.netlify/git"
squash_merges: true
accept_roles: #optional - accepts all users if left out
- admin
- editor
- user

display_url: https://publiuslogic.com
logo_url: https://publiuslogic.com/img/apple-touch-icon-180x180.png

publish_mode: editorial_workflow
media_folder: static/img
public_folder: /img

collections:

  • name: "pages"
    label: "Pages"
    files:

    • file: "src/pages/index.md"

      label: "Home Page"

      name: "home"

      fields:



      • {label: "Template Key", name: "templateKey", widget: "hidden", default: "home-page"}


      • {label: Title, name: title, widget: string}


      • {label: Heading, name: heading, widget: string}


      • {label: Description, name: description, widget: string}


      • {label: Offerings, name: offerings, widget: object, fields: [{label: Blurbs, name: blurbs, widget: list, fields: [{label: Image, name: image, widget: image}, {label: Text, name: text, widget: text}]}]}


      • {label: Testimonials, name: testimonials, widget: list, fields: [{label: Quote, name: quote, widget: string}, {label: Author, name: author, widget: string}]}


      • {label: "Meta Title", name: "meta_title", widget: "string"}


      • {label: "Meta Description", name: "meta_description", widget: "text"}



    • file: "src/pages/about/index.md"

      label: "About"

      name: "about"

      fields:



      • {label: "Template Key", name: "templateKey", widget: "hidden", default: "about-page"}


      • {label: "Title", name: "title", widget: "string"}


      • {label: "Body", name: "body", widget: "markdown"}


      • {label: "Meta Title", name: "meta_title", widget: "string"}


      • {label: "Meta Description", name: "meta_description", widget: "text"}



    • file: "src/pages/pricing/index.md"

      label: "Pricing Page"

      name: "pricing"

      fields:



      • {label: "Template Key", name: "templateKey", widget: "hidden", default: "pricing-page"}


      • {label: Title, name: title, widget: string}


      • {label: Image, name: image, widget: image}


      • {label: Pricing, name: pricing, widget: object, fields: [{label: Heading, name: heading, widget: string}, {label: Description, name: description, widget: string}, {label: Plans, name: plans, widget: list, fields: [{label: Plan, name: plan, widget: string}, {label: Price, name: price, widget: string}, {label: Description, name: description, widget: string}, {label: Items, name: items, widget: list}]}]}


      • {label: "Meta Title", name: "meta_title", widget: "string"}


      • {label: "Meta Description", name: "meta_description", widget: "text"}



    • file: "src/pages/contact/index.md"

      label: "Contact Page"

      name: "contact"

      fields:



      • {label: "Template Key", name: "templateKey", widget: "hidden", default: "contact-page"}


      • {label: Title, name: title, widget: string}


      • {label: Subtitle, name: subtitle, widget: string}


      • {label: Contacts, name: contacts, widget: list, fields: [{label: Email, name: email, widget: string}, {label: Description, name: description, widget: string}]}


      • {label: "Meta Title", name: "meta_title", widget: "string"}


      • {label: "Meta Description", name: "meta_description", widget: "text"}



  • name: "blog"
    label: "Blog"
    folder: "src/pages/blog"
    create: true
    slug: "{{slug}}"
    fields:

    • {label: "Template Key", name: "templateKey", widget: "hidden", default: "article-page"}

    • {label: "Title", name: "title", widget: "string"}

    • {label: "Slug", name: "slug", widget: "string"}

    • {label: "Publish Date", name: "date", widget: "datetime"}

    • {label: "Cover", name: "cover", widget: "image"}

    • {label: "Body", name: "body", widget: "markdown"}

    • {label: "Tags", name: "tags", widget: "list"}

    • {label: "Meta Title", name: "meta_title", widget: "string"}

    • {label: "Meta Description", name: "meta_description", widget: "text"}`

Go to My Gatsby Starter Publius

And use the code form there.

Most likely a error in your template or config file.

Thanks @jonniebigodes that helped solve the issue!

@jonathancary no need to thank. Glad i could be of assistance. And you managed to solve your issue

@jonniebigodes been scratching my head for hours facing this issue and your solution totally solved it. THANKS!!!!

@shanqyeet no need to thank, glad the reproduction helped you out aswell.

wow. after hours of searching this problem, I realize I had a special character in one of the image path like:

featuredimage: /img/file-with-an-é.png

I think that gatsby-remark-relative-images is having trouble with special characters in path

EDIT: I missed this component when I was cleaning up content from the starter template https://github.com/netlify-templates/gatsby-starter-netlify-cms/blob/master/src/components/PreviewCompatibleImage.js. Someone already wrote this code :)

Old comment:
I was only having issues in the Netlify CMS preview view. I finally just checked if the image was a string instead of the fluid object:

typeof image === 'string' ? (
  <img src={image} alt={alt} />
) : (
  <Img fluid={image.childImageSharp.fluid} alt={alt} />
)

I got the idea from https://theleakycauldronblog.com/blog/problems-with-gatsby-image-and-their-workarounds falling back to publicURL.

I'd love to find a nicer solution.

@jonniebigodes I ran into this same issue, thank you for the robust solve. However options that I'm setting for gatsby-remark-images are not being applied to said featured image. They're working on other images from the same md post, just not the frontmatter based featured image.

My config setup for the plugin:

...
{
   resolve: "gatsby-transformer-remark",
      options: {
        plugins: [
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 1024,
              backgroundColor: "#2fdf29", // for testing
              quality: 100,
              disableBgImageOnAlpha: true,
              // trying to disable the blur effect for images I have knocked out
            }
          },
          {
            resolve: "gatsby-remark-responsive-iframe"
          },
          "gatsby-remark-copy-linked-files",
          "gatsby-remark-autolink-headers",
          "gatsby-remark-prismjs",
          "gatsby-remark-check-links",
        ]
      }
    },
...
Was this page helpful?
0 / 5 - 0 ratings

Related issues

Oppenheimer1 picture Oppenheimer1  ·  3Comments

mikestopcontinues picture mikestopcontinues  ·  3Comments

jimfilippou picture jimfilippou  ·  3Comments

3CordGuy picture 3CordGuy  ·  3Comments

andykais picture andykais  ·  3Comments