Atlassian for providing the awesome venue!
You can also read the Twitter thread version of this post.
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.
One of these clients is... my wife Heather. She can literally shoulder tap 24/7 😅
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 🤗
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:
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:
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:
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.
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:
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.
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.
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!
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:
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:
We did the same for authors, tags, etc:
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!
Traditionally, bringing content editing to a project means:
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".
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.
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.
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!
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 👏👏👏
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
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!
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 👍
You can also pick between various format (YAML, JSON) for your data.
And choose if you want multiple files, or a single "Frontmatter + content" output, via the "contentField" option.
Again, this lets you fit a wide range of scenarios!
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
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 KeystaticThe code is simple enough to immediately grasp how to read/render Keystatic data with the built-in Reader & Renderer APIs 🔥
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
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 ❤️
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! ❤️
article
· 11 min readarticle
· 16 min readarticle
·article
·talk
screencast
article
· 11 min readarticle
· 16 min readarticle
·article
·talk
screencast
article
· 11 min readarticle
· 16 min readarticle
·article
·talk
screencast
Have a chat with one of our co-founders, Jed or Boris, about how Thinkmill can support your organisation’s software ambitions.
Contact us