Integrating Sanity with 11ty: troubleshooting and solutions

3 replies
Last updated: Feb 10, 2025
Are there any up-to-date guides for setting up Sanity with 11ty (Eleventy)?
I'm having a lot of trouble finding a good guide on integrating Sanity with 11ty. Specifically, I want to know how to get content in JSON format from Sanity into the _data directory of 11ty given a GROQ query that the Sanity "Getting Started" guide shows how to make upon signing up.

The following guide seems to rely on an out-of-date link to
https://www.sanity.io/create and, thus, doesn't show the whole integration setup: https://www.sanity.io/guides/how-to-get-started-with-the-11ty-eleventy-blog-starter
The following guide seems to have something useful in the "Eleventy Global Data" section, but I am unsure if the part about the query is all that's needed to have 11ty continuously monitor for any changes made in Sanity's content:
https://www.sanity.io/blog/migrating-netlify-blog-hugo-eleventy-using-sanity#f3cfa6179abb
Any advice and (creation of) relevant resources would be helpful.
AI Update

I understand your frustration - you're right that the existing guides are outdated. The 11ty starter guide from 2020 references the old sanity.io/create link, and while the Hugo migration blog post has useful pieces, neither provides a complete modern setup.

Let me walk you through the current approach for integrating Sanity with Eleventy (11ty), focusing on getting content into your _data directory with GROQ queries.

The Core Integration Pattern

The "Eleventy Global Data" section you found is actually the right approach - it's still valid today. Here's how to set it up properly:

1. Install the Sanity Client

First, install the current @sanity/client in your Eleventy project:

npm install @sanity/client

2. Create a JavaScript Data File

In your _data directory, create a file like posts.js. This file will fetch content from Sanity:

const {createClient} = require('@sanity/client');

const client = createClient({
  projectId: 'your-project-id',
  dataset: 'production',
  apiVersion: '2024-01-01', // Use current date
  useCdn: true
});

module.exports = async function() {
  const query = `*[_type == "post"] | order(publishedAt desc) {
    _id,
    title,
    slug,
    publishedAt,
    excerpt,
    body
  }`;
  
  const posts = await client.fetch(query);
  return posts;
};

This makes your posts available in templates as {{ posts }}. You can use whatever GROQ query the Getting Started guide showed you - just plug it into the query variable.

3. About Continuous Monitoring

Here's the important part: Eleventy's _data files run at build time, not continuously. They fetch data when you build your site, not in real-time. For content updates to appear, you need to trigger rebuilds.

Handling Content Updates

The standard, production-ready approach for keeping your static site in sync with Sanity content changes is to use GROQ-powered webhooks. Set these up in your Sanity project's API settings to trigger a rebuild whenever content changes:

  1. Go to your Sanity project dashboard at manage.sanity.io
  2. Navigate to API settings β†’ Webhooks
  3. Create a new webhook pointing to your hosting provider's build hook URL (Netlify, Vercel, etc.)
  4. Configure a GROQ filter to only trigger on relevant content changes, like:
    _type == "post"

This way, whenever a post is created, updated, or deleted in Sanity, your hosting provider automatically rebuilds your 11ty site with the fresh content. Webhooks are the stable, proven solution that Sanity has offered for years for external integrations.

Caching for Development

During local development, you can use @11ty/eleventy-fetch to cache API responses and avoid hitting rate limits:

npm install @11ty/eleventy-fetch

Then update your data file:

const EleventyFetch = require("@11ty/eleventy-fetch");
const {createClient} = require('@sanity/client');

const client = createClient({
  projectId: 'your-project-id',
  dataset: 'production',
  apiVersion: '2024-01-01',
  useCdn: true
});

module.exports = async function() {
  const query = `*[_type == "post"] | order(publishedAt desc) {
    _id,
    title,
    slug,
    publishedAt,
    excerpt,
    body
  }`;
  
  // Fetch from Sanity
  const posts = await client.fetch(query);
  
  // Cache the results - shorter duration in dev, longer in production
  return EleventyFetch(Promise.resolve(posts), {
    duration: process.env.ELEVENTY_ENV === 'production' ? '1h' : '1m',
    type: 'json'
  });
};

This caches responses locally so you don't re-fetch on every build during development. Note that you're using require with CommonJS syntax here, which is the standard approach for Eleventy data files.

Complete Modern Setup Steps

  1. Set up your Sanity project using npm create sanity@latest
  2. Install dependencies in your 11ty project: npm install @sanity/client @11ty/eleventy-fetch
  3. Create data files in _data/ that fetch from Sanity using the pattern above
  4. Configure webhooks in your Sanity project to trigger rebuilds on content changes
  5. Deploy to Netlify/Vercel with build hooks configured

The Key Insight

Eleventy generates static HTML at build time, so you're not "continuously monitoring" for changes in real-time within your 11ty code. Instead, you trigger new builds when content changes in Sanity. This is the standard Jamstack pattern.

The GROQ query in your _data file is all you need for fetching content - the "continuous monitoring" happens via webhooks that trigger rebuilds, not in the Eleventy code itself. The guides you found show the right data-fetching approach, they just don't explicitly explain the rebuild trigger mechanism.

If you need more advanced automation scenarios (like content validation, enrichment, or complex workflows that run before content is published), you could also explore Sanity Functions, though be aware these are currently an experimental feature with APIs subject to change. For most 11ty integration needs, the webhook approach is the stable, proven solution that doesn't require any additional infrastructure on your end.

Hi thereπŸ‘‹
I'm not able to find any more up-to-date guides at the moment either.

However, we should be able to build out a JS file in the
_data
directory to fetch the data. I'm not able to test this at the moment, but it should look something like this

const sanityClient = require('@sanity/client')
require('dotenv').config()

const client = sanityClient.createClient({
  projectId: process.env.SANITY_PROJECT_ID,
  dataset: process.env.SANITY_DATASET || 'production',
  apiVersion: '2024-02-10',
  useCdn: false
})

const postsQuery = `*[_type == "post"]`

async function getPosts() {
  try {
    return await client.fetch(postsQuery)
  } catch (error) {
    console.error('Error fetching posts:', error)
    return []
  }
}

module.exports = getPosts
Thank you so much for your help!
I think I finally got it to work with the addition of .createClient as you mentioned in my code I adjusted based on what I found at
https://www.bryanleetc.com/adding-sanity-io-headless-cms-to-eleventy/
I can see the CMS post in the frontend now.
After some more digging around related issues, I realize I should've looked up the docs of @sanity/client πŸ˜…

https://www.sanity.io/docs/js-client
In any case, I appreciated the help earlier.

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?