Skip to content

Blog · Jun 23rd, 2023 · 17 min read

Simon Vrachliotis avatar Simon Vrachliotis avatar

Simon Vrachliotis

7 Things I Love About Keystatic — A Thread...

After using Keystatic full-time for about 5 months, I thought I’d share some of the things I really like about it. The talk was at the SydJS meetup, held on the 21st of June 2023 at the Atlassian HQ.

First of all, shoutout to the SydJS Meetup and Sharkie for hosting the event, and to Atlassian for providing the awesome venue!

You can also read the Twitter thread version of this post.


Title slide for the talk, with screenshots of a few sites built with Keystatic Title slide for the talk, with screenshots of a few sites built with Keystatic

Over the past months, I’ve done a whole lot of exploration/prototyping with Keystatic. But I have also shipped real things.

Real production projects, with real clients. With real needs/shoulder tapping.

A few websites I built with Keystatic A few websites I built with Keystatic

One of these clients is... my wife Heather. She can literally shoulder tap 24/7 😅

My wife’s website, Star Athletics My wife’s website, Star Athletics

These months of exploration and shipping stuff give me a well-deserved certification star ⭐️ to talk with "authority" about Keystatic, and what I really love about it.

Let’s dive in 🤗


Slide for chapter 1 of the talk Slide for chapter 1 of the talk

#1 — Keystatic doesn’t really change how I would build websites for myself

After showing off my puppies in slow motion on the big screen, I presented my first key point I love about Keystatic: the fact that Keystatic doesn’t really change how I would build a website for myself.

I used this Tailwind UI component as an example:

Pricing plans UI Pricing plans UI

The way I’d build this is exactly how the code is set up: a JavaScript array of objects to manage the "data" for each pricing plan:

JavaScript array of pricing tiers JavaScript array of pricing tiers

That’s great and easy to edit for developers. But what if my non-technical friend wants to edit?

You could argue that it’s "not that hard" to edit strings inside a JS object.

And you’re right.

But there’s a lot of "baggage" that comes with it for non-technical folks:

  • open a JSX file in a code editor
  • push the code to GitHub

We take those things for granted.

To help my friend out, I will re-create the pricing plan data structure in a Keystatic schema.

I create a "pricing" singleton in the Keystatic config, and set the schema to mirror the shape of the pricing tiers.

Keystatic config code snippet Keystatic config code snippet
Keystatic config code snippet Keystatic config code snippet

With that config in place, I can tell my friend to visit the /keystatic route on the website. They’ll find a Pricing section where they can create some pricing tiers ✨

Data entry in the Keystatic Admin UI

And here’s what Keystatic outputs:

The generated JSON file The generated JSON file

A pricing.json file inside src/content, just like we defined in our config.

This is pretty neat!

But my friend still needs to manually enter the pricing tiers... which already exist in code.

What if instead we made the existing tiers the thing that Keystatic can edit?

Keystatic now writes (but also reads!) from that "pricing.json" file. Let’s move our tiers in that file!

Porting the hardcoded JavaScript tiers array into our Keystatic-managed JSON file

And just like that, we made our hardcoded tiers array editable with Keystatic!

Our original pricing tiers now in Keystatic!

All the data shows up in the Admin UI, and my friend can start editing the plans, instead of creating them again from scratch.

Neat 🎉

Ok, so my friend did some changes. Renamed the "Basic" plan to "Keystatic", and added a feature item.

Before we can see this on the front end, we need to adjust our template code.

And it’s a very minimal change!

All we need to do is replace the hardcoded tiers array with the new tiers from the "pricing.json" file.

Minimal code change in our template

That’s it. Same variable name, so the rest of the code still works exactly the same way!

Now, if we visit our Pricing page once again, we can verify that the plans are indeed showing the changes made in Keystatic:

Our UI is updated ✨

All we did was create a schema mirroring the data shape, and import the JSON output in our page. That’s it.

We’ve made life infinitely better for our non-technical friends.

But as developers, we can still work the same way, and edit the JSON data manually. We don’t have to use the Admin UI.

It’s there for those who need it.

And that’s pretty cool 😎

Subscribe for updates

We send a newsletter from time to time when we publish new resources, articles, and open source projects on the topics of software design and engineering, design systems, and process & practice.

Slide for chapter 2 of the talk Slide for chapter 2 of the talk

#2 — The TypeScript self-documenting dev experience

When I learned Keystatic (very early days), there was absolutely no documentation.

Besides asking questions to the core team — and I sure did 😅 — my primary way to learn: autocomplete TypeScript discovery.

Autocomplete suggestions from Types Autocomplete suggestions from Types

Here’s a video recording of creating a new schema in the Keystatic config. It almost feels like between GitHub Copilot and TypeScript, all I need to do to write a schema is hit one key and then press "tab tab tab" to let AI build my schema 🤣

TypeScript and GitHub Copilot doing all the work for me

Those Types also come handy when pulling content in the frontend!


Slide for chapter 3 of the talk Slide for chapter 3 of the talk

#3 — Keystatic works well with the tool I already use and love.

So far, I built sites with Next.js and Astro. Keystatic’s flexibility makes it pair with those really nicely.

Let’s take the example of Astro. Astro has that built-in concept of "Content Collections". Essentially a "content" directory in your project, that will let you create "query-able" data collections:

Astro’s content collections Astro’s content collections
Astro’s content collection folders Astro’s content collection folders

It allows you to query data from files in your file system using built-in helpers:

---
import { getCollection } from 'astro:content'
const blogPosts = await getCollection('blog')
---

<ul>
  {blogPosts.map(post => <li>{post.title}</li>)}
</ul>

We had already built the Thinkmill website with Astro and Content Collections before thinking about bringing Keystatic into the picture. And so, we wanted to keep leveraging those content collections.

By setting the output "path" to "src/content/blog" in our Kesytatic config, we were able to do just that:

Keystatic blog post config Keystatic blog post config
Keystatic blog post output Keystatic blog post output

We did the same for authors, tags, etc:

content collections folder structure content collections folder structure

And It Just Worked™

Out of the box.

🤯

And that’s probably the coolest thing about Keystatic:

It can meet you where you are.

Whatever way you’re building stuff — it find ways to adapt, and be helpful without getting in the way.

Keystatic actually does have its own powerful Reader API to query the file system... But it’s not needed here!


Slide for chapter 4 of the talk Slide for chapter 4 of the talk

#4 — Keystatic travels light, doesn’t bring much "devOps baggage"

Traditionally, bringing content editing to a project means:

  • bringing a database
  • needing to maintain said database
  • refactoring a bunch of stuff

What about our Astro Content Collections? Most CMS won’t output their content inside the src/content/blog directory like we want.

You’ll have an API or ORM to hit to get your data. Which is fine, but it may be a departure from your existing setup.

Thanks to Keystatic’s flexibility, in my experience, you often don’t need to add that extra "baggage".


Slide for chapter 5 of the talk Slide for chapter 5 of the talk

#5 — With Keystatic, you include non-technical folks within your developer-first workflow

You don’t invent/force a new system to allow content editing, and then force the dev team to use that new system.

You stay in your wheelhouse, and bring new folks along.

If you’re using MDX, you may assume that it’s fine for non-technical devs to edit MDX file.

It’s really not.

There’s a bunch of weirdness, module imports, bracket syntax, passing props etc.

Markdown could work with non-technical people, but MDX probably not.

Quote suggesting to edit MDX files for non-developers — probably not a good idea Quote suggesting to edit MDX files for non-developers — probably not a good idea

So, if non-technical colleagues can’t do copy changes... instead they’ll do "change requests" tickets. In Notion, Jira, on your desk.

Post-It on your screen.

Weird feelings of "nagging" will creep in. Both ways.

Trust me, your non-technical colleagues don’t enjoy nagging you. They want autonomy!

Keystatic allows you to empower them to make these changes, without making sweeping changes to the way you work as a developer.

It makes your codebase more inclusive and accessible.

Is this... a11y? 😮🫱🦋 Is this... a11y? 😮🫱🦋

What do I mean by "bring non-technical folks in our dev-first workflow"?

Keystatic deeply integrates with GitHub. When a non-technical person edits content in Keystatic, they actually create a branch, make commits, open a PR, etc.

Deploy previews, CI pipeline, all of it ✨

Let’s take a fictive character for a non-technical person interacting with Keystatic.

We’ll call our character Sharkie.

Oh, hey @twalve 👋

Sharkie has a GitHub account with access to our repo, so they can "log in" and access the Admin UI.

GitHub login to access the Keystatic Admin UI

Keystatic lets you teach lightweight concepts about dev workflows — in an environment that feels comfortable for folks like Sharkie ❤️

Concept like branches.

I explain that instead of changing the live site (main branch), they should create a new "branch" (preview).

After creating a new branch, Sharkie can create a new author, and write a blog post:

After creating the author and the post, the UI shows two little 1️⃣ badges.

We, as developers, can read through this: The "demo-sharkie" branch is two commits ahead of main.

Sharkie doesn’t need to know this. The info here is:

Authors and Blog Popsts have both 1 new thing

I explain to Sharkie that to suggest adding those changes to the live site, they should "create a pull request".

This is how developers communicate intent to "pull" new code into a project.

Still a weird concept for non-devs, but at least wrapped in a non-intimidating UI ✨

Sharkie opens a pull request without needing to care about GIT stuff

The Pull Request video in the previous tweet is a great reminder that Keystatic is "just a wrapper" around GitHub workflows.

Without knowing it, Sharkie has created two Markdoc files with frontmatter metadata, and prepared those files into a PR.

Just as if it was done manually!

Screenshot of GitHub pull request changes Screenshot of GitHub pull request changes

Who does the next step is up to you.

We can take over and handle the PR. But it’s a nice opportunity to teach a bit more about GitHub workflows to our friend Sharkie.

The reward? They can discover the world of deploy previews, GitHub Actions etc.

You can blow their mind with that 🤯

Triggering a deploy preview build

Not only does this make Sharkie more fluent with dev jargon — and a better communicator with dev teams...

It also gives them insights on where to find the preview URL. And also answer their "why are my changes not showing up immediately" question.

They see the build process 👀

When the site has finished deploying, Sharkie can visit the deploy preview:

Branch preview on Vercel

Knowing that this version of the site is not live, but they can see it and share it with the stakeholders, board of directors, their neighbour, or anyone...

... is fundamentally cool.

Look at Sharkie go...

Opening PRs and triggering Vercel deploys 👏👏👏


Slide for chapter 6 of the talk Slide for chapter 6 of the talk

#6 — Local mode is my JAM 🍯

As cool as that GitHub integration is… my JAM in Keystatic is local mode 👀

Think of it as a stripped-down, minimal, "power user" version of Keystatic.

No GitHub, no PRs.

Editing your file system directly.

Switching Keystatic to `local` mode

Minimal UI without GitHub context Minimal UI without GitHub context

Why do I love this?

The feedback loop (hot reload on changes) is much faster.

This is amazing for prototyping, and also teaching/demo-ing Keystatic 🔥

You’ll rarely catch me using Keystatic in another mode 😅

A great setup is to do local mode for dev, github mode for pro ⭐️

Here’s a demo of that immediate feedback loop I am talking about.

Edit content, and the UI is instantly updated:

Content editing DX with local mode

Remember: this is a two-way street: edit the code — and the UI is also immediately updated!

Instant update when editing the code as well!


Slide for chapter 7 of the talk Slide for chapter 7 of the talk

#7 — Flexibility of config settings for file location, format

As we’ve seen, you can decide where you output any collection.

You can also choose where images go, independently of the collection settings.

Helps you fit framework-specific needs 👍

Path configuration Path configuration
Image directory configuration Image directory configuration

You can also pick between various format (YAML, JSON) for your data.

Format configuration Format configuration

And choose if you want multiple files, or a single "Frontmatter + content" output, via the "contentField" option.

Keystatic collection multi-file output Keystatic collection multi-file output
contentField configuration contentField configuration
Single file output Single file output

Again, this lets you fit a wide range of scenarios!

Here’s a pretty advanced use-case, inside a monorepo

This is a prototype for the Earth Design System, for one of the clients we work with Thinkmill.

We use Keystatic to power the documentation of Design Systems components.

We want to collocate the docs with each component:

Earth Design System — Component documentation managed by Keystatic

Noticed the Button components inside the WYSIWYG document field in the previous video?

Yeah, those are the real buttons — not some screenshots or representations of the Buttons.

You can use React components as custom blocks inside the document editor.

Anyway — at the end of that video we set the path to packages/core/src/*/docs.

Let’s find why.

The * symbol is where the slug for each collection entry will go.

If you edit the Button docs page, it will go in packages/core/src/button/docs.

Here’s our project tree:

The monorepo project tree

See what happens?

From the Keystatic site, located in a very different place in the monorepo, we’re able to output the Keystatic-generated documentation for each component alongside the button source code, tests, stories...

How cool is that?!

Despite the fact that Keystatic and the UI components are in totally different folders, we retain this "two-way editing" experience.

Change code via code, or via the Admin UI. It hot reloads immediately either way.

🤯

Two-way editing within a monorepo


Single file output Single file output

#8 — The new Keystatic CLI 😻

Yes — I know the title says "7 Things".

But a new one came up recently, and it definitely belongs as the grand finale!

(That, plus I have 8 puppies — which makes this whole count increment decoration concept work great 😅)

Introducing... the new Keystatic CLI 🔥

It encapsulates all of the great little things I love about Keystatic:

lightweight, no "baggage", get started in seconds. It underlines and promotes the "two-way" editing experience

It is sweet as hell!

npm create @keystatic

This is definitely the recommended way to try Keystatic out.

You can try it right now!

It will spin up a brand new Next.js project with Keystatic setup in local mode.

We plan to support more frameworks (Astro, Remix, ...) or existing projects soon ✨

New Keystatic project from the CLI

The project it spins up is delightfully minimal:

  • Posts collection in Keystatic
  • homepage listing that one post
  • "single post" view to read the post

The code is simple enough to immediately grasp how to read/render Keystatic data with the built-in Reader & Renderer APIs 🔥

Reader API code snippet Reader API code snippet
Renderer API code snippet Renderer API code snippet

As I mentioned, I like how the single post page invites you to think about this "two-way" editing.

Let’s try both — and see how fast the UI updates for us!

Two-way editing in the Keystatic CLI new project


Aaaannnd... that’s it!

I’ve got PLENTY more to say about Keystatic. So please, ask us anything, would love to hear your thoughts.

Check out Keystatic.com’s docs, try out the new CLI, and tell us what you think in the GitHub discussions.

Help us shape Keystatic ❤️

GitHub Discussions are a great place to get involved! GitHub Discussions are a great place to get involved!

If you’ve made it to the end of this MEGA post...

... I’m impressed.

You’re a LEGEND.

To reward you, here’s a fairly cute "Thank You" slide I used last night to wrap up my talk 🤗

The cutest closing slide you will ever see 🤗

Seeya! ❤️

Services discussed

  • Content Management Systems

Technology discussed

  • Keystatic,
  • TypeScript,
  • JavaScript,
  • Monorepo,
  • Next.js

Tagged in

Simon Vrachliotis avatar Simon Vrachliotis avatar

Simon Vrachliotis

@simonswiss (opens in new window)

Designer, developer, content creator, teacher, learner.

A photo of Jed Watson & Boris Bozic together A photo of Jed Watson & Boris Bozic together

We’d love to work with you

Have a chat with one of our co-founders, Jed or Boris, about how Thinkmill can support your organisation’s software ambitions.

Contact us