How to connect Gatsby Image with Airtable source plugin

I’m attempting to use gatsby-source-airtable to retrieve images from my Airtable database and display them using Gatsby Image.

Configuration

In my gatsby-config.js file, I’ve set up the attachment column as a file node:

mapping: {'image': 'File'}

Query works in GraphiQL

When I run the following query in GraphiQL:

{
  airtable(table: {
    eq: "table-1"
  }, data: {
    slug: {
      eq: "test-1"
    }
  }) {
    data {
      image {
        localFiles {
          childImageSharp {
            fluid(maxWidth: 400) {
              src
            }
          }
        }
      }
    }
  }
}

I receive this successful response:

{
  "data": {
    "airtable": {
      "data": {
        "image": {
          "localFiles": [{
            "childImageSharp": {
              "fluid": {
                "src": "/static/08baa0d1735184a4d0dd141d90f564d4-28158c2eb0b0b748efeabc0ec551c623-7eb65.jpg"
              }
            }
          }]
        }
      }
    }
  }
}

Issue in component

However, when I try to implement this in my component using Gatsby Image:

<Img fluid={post.data.image.localFiles.childImageSharp.fluid} />

export const query = graphql`
  query PostQuery {
    airtable(table: {
      eq: "table-1"
    }, data: {
      slug: {
        eq: "test-1"
      }
    }) {
      data {
        image {
          localFiles {
            childImageSharp {
              fluid(maxWidth: 400) {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  }
`

I encounter the error: WebpackError: TypeError: Cannot read property 'fluid' of undefined.

What might be the cause of this problem? Any suggestions would be appreciated!

Yeah, the other answer nailed the array issue, but here’s another gotcha - your app crashes hard if the Airtable record doesn’t have an image.

I hit this exact problem last year building a product catalog that pulled from Airtable. You’ve gotta handle the edge cases properly.

Check if the image exists and has files before rendering:

{post.data.image?.localFiles?.[0]?.childImageSharp?.fluid && (
  <Img fluid={post.data.image.localFiles[0].childImageSharp.fluid} />
)}

But this gets messy fast when you scale up. I ended up automating the whole image pipeline with Latenode instead. It watches my Airtable for new records, processes images automatically, and pushes optimized versions to a CDN.

No more worrying about missing images or complex GraphQL queries. The automation handles image resizing, format conversion, and even generates alt text. Way cleaner than debugging Gatsby’s image processing quirks.

The problem is you’re treating localFiles like a single object when it’s actually an array. Your GraphQL query returns an array with one element, but you’re trying to access childImageSharp directly on the array instead of the first element. Fix it like this: jsx <Img fluid={post.data.image.localFiles[0].childImageSharp.fluid} /> See that [0]? That grabs the first item in the localFiles array, which matches what you’re seeing in GraphiQL. I hit this exact same wall when I started using Airtable attachments with Gatsby. The plugin always creates an array even for single images, which is weird since most Airtable attachment fields only have one file. Just remember to use the array index with localFiles from the Airtable plugin.

Check your gatsby-node.js setup too. I hit this exact error when my file mapping was misconfigured during createNode. GraphiQL sometimes shows data that doesn’t exist in your component context.

Try adding this to gatsby-node.js:

exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes } = actions
  createTypes(`
    type AirtableData {
      image: AirtableAttachment
    }
  `)
}

This forces Gatsby to recognize the attachment field structure. Without explicit schema definitions, the Airtable plugin creates inconsistent node relationships between dev and build environments.

I’ve seen cases where localFiles gets created but childImageSharp nodes aren’t linked during SSR. Also check that your Airtable images are actually processed - sometimes the plugin downloads files but Sharp fails to create childImageSharp nodes, leaving you with empty localFiles that GraphiQL doesn’t show accurately.

Quick tip - double check your gatsby-config mapping syntax. I had the same issue where my mapping wasn’t quite right and it worked in GraphiQL but failed in components. Make sure you have mapping: { 'YourFieldName.image': 'File' } if your Airtable field has a specific name, not just the generic mapping you showed.

The array indexing issue is spot on, but here’s another debugging trick that’s saved me tons of time with Airtable image setups.

Add console logging to see what data structure you’re actually getting:

console.log('Full image data:', post.data.image);
console.log('Local files:', post.data.image?.localFiles);

I’ve seen the Airtable plugin create the localFiles array but childImageSharp processing fails silently. The array exists but sharp transforms don’t complete, so you get undefined when accessing fluid.

This usually happens when:

  • Image files are corrupted in Airtable
  • File formats aren’t supported by Sharp (PDFs mixed in attachment fields)
  • Memory issues during build with large images

Run gatsby clean and rebuild. If that doesn’t work, check your original Airtable attachments. I once spent hours debugging only to find someone uploaded a 15MB PNG that choked the image processor.

Also verify your GraphQL fragment loads properly. Sometimes ...GatsbyImageSharpFluid doesn’t import correctly and you’ll see different behavior between GraphiQL and your component.