Blog with Sanity.io and 11ty
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?
Permalink to “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
Permalink to “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
Permalink to “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
Permalink to “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
Permalink to “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
Permalink to “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
Permalink to “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
Permalink to “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
Permalink to “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
Permalink to “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
Permalink to “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/{% if pagination.pageNumber > 0 %}page-{{ pagination.pageNumber + 1 }}{% endif %}/index.html"
---
<div>
<div>
<div>
<h1>Journal</h1>
<p>Thoughts, experiments and other ramblings.</p>
<ol>
{% for post in pagination.items %}
{% set currentPost = post.data.post %}
{% if post.url != url %}
<li>
<h2>
<a href="{{ post.url | url }}">{% if currentPost.title %}{{ currentPost.title }}{% else %}<code>{{ post.url }}</code>{% endif %}</a>
</h2>
<div>
{{ currentPost.body | truncate(200) }}
</div>
<p>
<time datetime="{{ currentPost.publishedAt }}">{{ currentPost.publishedAt|readableDate }}</time>
</p>
</li>
{% endif %}
{% endfor %}
</ol>
{% if pagination.pages.length > 1 %}
<nav aria-labelledby="journal-pagination">
<h2 id="journal-pagination" class="u-sr-only">Journal pagination</h2>
<ol>
<li>
{% if pagination.href.previous %}
<a href="{{ pagination.href.previous }}">
Previous
</a>
{% else %}
<span>
Previous
</span>
{% endif %}
</li>
{%- for pageEntry in pagination.pages %}
<li>
<a href="{{ pagination.hrefs[ loop.index0 ] }}"{% if page.url == pagination.hrefs[ loop.index0 ] %} aria-current="page"{% endif %}>
Page {{ loop.index }}
</a>
</li>
{%- endfor %}
<li>
{% if pagination.href.next %}
<a href="{{ pagination.href.next }}">
Next
</a>
{% else %}
<span>
Next
</span>
{% endif %}
</li>
</ol>
</nav>
{% endif %}
</div>
</div>
</div>
Deploying
Permalink to “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
Permalink to “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
Permalink to “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
Permalink to “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
Permalink to “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.
Written on