Playing around with

2022 - 12 - 12
Playing around with is one of the new cool kids regarding headless CMSs. I decided to give it a try and create almost precisely the same blog as the one you are reading.

What is

First of all, is a headless CMS. This means it's only responsible for storing the data, and it's up to us to connect it with a front end. We can use whatever we want here.

The CMS is fully configurable. Thanks to Sanity Studio, we can configure our CMS how we want - we just need to create schemas.

Sanity also gives us two ways to communicate with our application: GraphQL or GROQ. I just want to say that GROQ quickly became my favorite. It's like SQL and GraphQL had a baby.

Now that we know what is, it's time to code.

The plan

I plan to create a blog like the one you are just reading. This means we will need the following:

There are also some things that I decided not to add because it's just an experiment:

Time to start modeling this content using Sanity Studio

Installation and some groundwork

Installation is pretty simple. Just follow the steps from the official guide, and everything should be up and running in a few minutes.

In my case, I also installed one addon - the markdown input field.

And here is the code repository. In the studio folder, you'll find everything related to Sanity. Apart from this folder, everything else is associated with Astro.

Adding schemas

My schema structure looks like this:

|-- documents
|-- objects

The document folder is for post types, and the objects folder is for small bits that we will reuse from time to time.

For example, let me show you how to create a category schema. First, create an empty file category.js inside the schemas/documents folder. Then add this code inside:

export default {
    name: "category",
    type: "document",
    title: "Category",
    fields: [
            name: "title",
            type: "string",
            title: "Title",
            name: "slug",
            type: "slug",
            title: "Slug",
                "Some frontends will require a slug to be set to be able to show the category",
            options: {
                source: "title",
                maxLength: 96,

As you can see, it consists of two fields - Title and Slug.

Next, it's time to import it the index.js file:

import category from "./documents/category";

export const schemaTypes = [category];

And is how our admin panel will look like:

You can browse the rest of the fields in the GitHub repo.

Connecting with Astro

Now, it's time to connect with Astro.

I won't go step-by-step because everything becomes simple if you understand how the GROQ language works.

First, we need to install the Sanity + Astro plugin. Kudos to Jaydan Urwin for all his hard work. After installing the plugin, I created a /src/utils/sanityHelper.js file to add all the helper functions I need.

So, let's get the navigation. First of all, I created a helper:

export async function getNavigation(navigationName) {
    const query = `*[_type == "nav" && id == "${navigationName}" ]`;
    const navigation = await useSanityClient().fetch(query);
    return navigation;

It fetches all the posts from the nav post type and the passed navigation id.

Next, in my header component I'm doing something like this:

import { getNavigation } from "../utils/sanityHelper.js";
const navigation = await getNavigation( 'header_menu' );
<ul class="font-bold font-palmiak-font tracking-widest">
{navigation[0] => (
    <MenuHeaderMobile href={item.navItemUrl.linkUrl}>{item.text}</MenuHeaderMobile>

... and that's it 😄

Fetching posts is similar. There are some gotchas.


In the helper file, you'll find the urlForImage function. To use, you have to use:


Also, check the sanity/image-url documentation. You'll learn more about all its possibilities.

Markdown fields

Astro, by default, doesn't have any method to show markdown data that was fetched from an API. To fix this, we have you use the marked package. This is how I displayed post content:

import { marked } from 'marked';
const { post } = Astro.props as Props;
<div set:html={marked.parse(post.body)} />

Querying references

When I wanted to display category names in every post, I hit a small bump on the road. Luckly it was easy to solve.

This is what my initial query looked like:

*[_type == "post" ] {
} | order(publishedAt desc)

to get all the data related to each category, I had to add []->

*[_type == "post" ] {
} | order(publishedAt desc)

Check out this page for more information.

That's it

I hope you'll also find as friendly and easy to use as me. Of course, this article just scratches the surface of what can do and how we can configure it.

Don't forget to share what you have done using

Subscribe to my newsletter and stay updated.
Get an weekly email with news from around the web
Get updated about new blog posts
No spam

Share your thoughts

All Articles