Blog with Sanity.io and 11ty
Posted on:
This website has been recently rebuilt with 11ty. I quite like having a simple setup without a CMS for my personal site. It’s flexible and easy to experiment with I did however want a simple way to write and manage articles. The ideal time to run a little experiment with Sanity.io.
What does sanity.io do?
It’s a content platform similar to a content management system (Wordpress, CraftCMS, …) but without a front-end attached. You simply define your content structure which you can then access from whatever system you want. It’s really lightweight and quick to set up.
Setting up Sanity Studio
I created a free account that will cover creating a personal blog. I’m not going to describe the process of setting up the studio as they do an excellent job with their getting started documentation.
Schema
As a starting point I created a simple schema where I opted to use the markdown editor instead of the default richt text editor:
export default {
name: 'post',
title: 'Post',
type: 'document',
fields: [
{
name: 'title',
title: 'Title',
type: 'string',
},
{
name: 'slug',
title: 'Slug',
type: 'slug',
options: {
source: 'title',
maxLength: 96,
},
},
{
name: 'publishedAt',
title: 'Published at',
type: 'datetime',
},
{
name: 'body',
title: 'Body',
type: 'markdown',
},
]
}
Hosting
Once I got it set up I deployed it with Netlify. It’s nice that they provide the option to self-host the client. The API itself is hosted by Sanity.
Access settings
One important thing to note is that you need to define the access settings to the API under the “CORS Origin” settings in the Sanity dashboard. If you self host you need to give that domain write access.
Getting content into 11ty
There's some setup involved for getting the content from the Sanity API.
- Setup the sanity client in 11ty
- Collecting the data
- Generating the pages in 11ty
- Creating an article template
- Adding an overview page
Setting up the sanity client in 11ty
First, you need the sanity client as a dependency
"@sanity/client": "^2.0.0"
Add an .env.development file to store the sanity token for local development. For production you need this .env file on your server or in a Github secret.
SANITY_READ_TOKEN=[YOUR_TOKEN]
SANITY_PROJECT_ID=[PROJECT_ID]
SANITY_DATASET=[DATA_SET]
Provide a client-config.js
file in the root of your project.
module.exports = {
sanity: {
projectId: process.env.SANITY_PROJECT_ID,
dataset: process.env.SANITY_DATASET,
apiVersion: '2021-08-31',
useCdn: true
}
}
Finally, setup the sanity client in utils/sanityClient.js
.
require('dotenv').config({
path: `.env.${process.env.NODE_ENV || 'development'}`
})
const sanityClient = require("@sanity/client");
const { sanity } = require('../client-config')
module.exports = sanityClient({
...sanity,
useCdn: !process.env.SANITY_READ_TOKEN,
token: process.env.SANITY_READ_TOKEN
});
Collecting the data
Adding a post.js
file in the data
folder of your 11ty project allows you to query the Sanity API. Here we get the published date, title, slug and body of a post.
const groq = require('groq')
const client = require('../../utils/sanityClient.js')
const hasToken = !!client.config().token
function generatePost (post) {
return {
...post,
}
}
async function getPosts () {
// Learn more: https://www.sanity.io/docs/data-store/how-queries-work
const filter = groq`*[_type == "post" && defined(slug) && publishedAt < now()]`
const projection = groq`{
publishedAt,
title,
slug,
body
}`
const order = `| order(publishedAt asc)`
const query = [filter, projection, order].join(' ')
const docs = await client.fetch(query).catch(err => console.error(err))
const preparePosts = docs.map(generatePost)
return preparePosts
}
module.exports = getPosts
Generating the pages in 11ty
To generate the pages, create a journal.njk
(the name of your article collection) file. This will output a collection of articles.
---
layout: post
section: Journal
permalink: journal/{{ post.slug.current | slugify }}/index.html
pagination: // The pagination generates the pages from the data
alias: post
data: posts
size: 1
addAllPagesToCollections: true
eleventyComputed: // You need to process this data to use it outside of the page content (ex. in meta data or a RSS feed)
title: "{{ post.title }}"
content: "{{ post.body }}"
---
Creating an article template
Create a layout for your articles. Here I also added a previous/next article navigation at the end of the current article.
---
layout: base
---
<article>
<p><a href="/journal/">Journal</a></p>
<h1>{{ post.title }}</h1>
{{ post.body | markdownify | safe }}
<hr />
<p>Published on <time datetime="{{ post.publishedAt }}">{{ post.publishedAt|readableDate }}</time></p>
</article>
<div>
{% set previousPost = collections.journal | getPreviousCollectionItem(page) %}
{% set nextPost = collections.journal | getNextCollectionItem(page) %}
{% if nextPost %}
<h2>Continue</h2>
<p>
<a href="{{ nextPost.url }}">{{ nextPost.data.title }}</a>
</p>
{% elseif previousPost %}
<h2>Continue</h2>
<p>
<a href="{{ previousPost.url }}">{{ previousPost.data.title }}</a>
</p>
{% endif %}
</div>
Adding an overview page
Finally, an overview page gives you an overview of the articles. These are generated from the collection we created.
---
layout: base
title: Journal
eleventyNavigation:
key: Journal
pagination:
data: collections.journal
size: 10
reverse: true
permalink: "journal//index.html"
---
<div>
<div>
<div>
<h1>Journal</h1>
<p>Thoughts, experiments and other ramblings.</p>
<ol>
</ol>
</div>
</div>
</div>
Deploying
Finally I wanted to trigger an 11ty build if I publish a post in Sanity. I already had a Github action set up to build and deploy 11ty on push. Sanity provides webhooks so the process is pretty painless.
Add a repository dispatch on the github action
on:
push:
branches:
- main
repository_dispatch:
types: [deploy] // This is the trigger we will use in the webhook
jobs:
...:
Write the webhook
You can create a webhook in the “API” section of the Sanity dashboard.
To send this to Github I used the github API.
- URL:
https://api.github.com/repos/[USER_NAME]/[REPO_NAME]/dispatches
- Trigger on:
create
,update
anddelete
- Filter:
_type == "post" && defined(slug) && publishedAt < now()
- Projection (the POST body):
{"event_type": "deploy"}
- HTTP method:
POST
- HTTP headers:
Accept
:application/vnd.github+json
Authorization
:token [PERSONAL_ACCESS_TOKEN]
- Make sure you create a github
PERSONAL_ACCESS_TOKEN
with access to theREPO
andWORKFLOW
.
Final thoughts
I must say I enjoyed using Sanity with 11ty. It was pretty straight forward and the way it allows you to craft the setup to your liking is really nice. There’s still some stuff I have to figure out like using the rich text editor, images and generating category pages but so far I’m happy with the result. I have the benefits of a static site and the ease of a CMS for writing articles.
Further reading
- Sanity.io has a good starter template: How to get started with the 11ty (Eleventy) Blog Starter
- Hidde wrote a great article on Making a photo blog with Sanity and 11ty.
Tagged with:
More posts:
Hi, nice to meet you!
Ghent, Belgium
Throughout the previous 21 years, my role has revolved around design and front-end development for websites and applications. My focus lies in crafting projects that prioritize sustainability, user-friendliness, and inclusivity.
You can hire me for:
- Design systems
- Pattern libraries
- Prototyping
- Custom CSS frameworks
- Accessibility
- Coaching
