When I first started building websites, it was all about templates and page builders. My first role out of college was at an agency where most projects revolved around WordPress. It was flexible enough for clients, but every new request meant another theme override or plugin. Later, when I moved to an in-house role, I didn’t touch a CMS for a while.
Back then, a CMS was just a way for us developers to get away from making small copy changes that the client could handle. Earlier this year, I picked up some freelance work and that’s exactly what I needed.
That’s when I came across Sanity, while browsing Reddit to see what indie developers were using. I figured I’d start by rebuilding my own site (I’d always wanted to launch a blog), and that experience flipped my ideas completely.
Sanity isn’t just a CMS—they truly live up to the idea of a content operating system. It’s not about managing pages, it’s about designing systems that adapt, automate, and scale.
In this post, I’ll share what I learned from Sanity Conf, how Blueprints and Functions are redefining custom content, and walk through how to build one yourself.
Shifting from Static to Composable
Web Development as I knew it when I first graduated in 2020 was a very manual process. There was no real connection between the systems just a patchwork of static processes.
With Sanity, content, automation, and logic all live in the same layer. Functions, Agent Actions, and Blueprints give teams the primitives to program their content infra rather than maintain it. Instead of configuring tools around your workflow, you start expressing intent directly in your content model.
For example, instead of wiring up an external webhook to summarize blog posts, you can define a document function that runs only when content changes.
At Sanity Conf, I got to see this shift firsthand. At the workshop, the Sanity team walked us through building automated content workflows using Functions and Blueprints. In addition to learning about the newest features Sanity shipped, we explored designing systems that responded to content events, performed AI-assisted tasks, and redefined what “content management” could mean. It was the first time I’d seen content behave like software. Designers, engineers, and marketing teams were able to design AI-first solutions tailored to their own workflows using the same underlying structure.
Building Your First Sanity Function
One of the clearest and most powerful examples of intent driven automation is Kevin Green’s Auto Summary Function. When a content editor publishes a new blog post without a summary the function automatically triggers on the publish event, analyzes the post content field using AI, generates a summary of the document body content and applies the summary directly to the published document.
This schema defines an autoSummary field that will automatically populate whenever a post is created or updated. It’s the destination for the text generated by the Auto Summary Function —giving editors a live, AI-generated summary directly inside Sanity Studio.
defineField({
name: 'autoSummary',
title: 'Auto Summary',
type: 'text',
description: 'Summary will be automatically generated when you publish a post',
}),This function listens for post creation or updates and automatically generates a short AI summary using Sanity’s built-in agent.action.generate method. It reads the post’s body field, creates a 250-word summary, and writes it back to the autoSummary field in the same document.
import {createClient} from '@sanity/client'
import {documentEventHandler} from '@sanity/functions'
export const handler = documentEventHandler(async ({context, event}) => {
const client = createClient({
...context.clientOptions,
apiVersion: 'vX',
useCdn: false,
})
const {data} = event
const {local} = context // local is true when running locally
try {
const result = await client.agent.action.generate({
noWrite: local ? true : false, // if local is true, we don't want to write to the document, just return the result for logging
instructionParams: {
content: {
type: 'field',
path: 'body',
},
},
instruction: `Based on the $content, write a summary no more than 250 words.`,
target: {
path: 'autoSummary',
},
documentId: data._id,
schemaId: '_.schemas.default',
forcePublishedWrite: true,
})
console.log(
local
? 'Generated summary (LOCAL TEST MODE - Content Lake not updated):'
: 'Generated summary:',
result.autoSummary,
)
} catch (error) {
console.error('Error occurred during summary generation:', error)
}
})Blueprints define how resources like Functions and Agent Actions are deployed and connected within your Sanity project. In this case, the blueprint registers the auto-summary function as a document-level resource that runs whenever a post is created or its content changes. The event filter ensures the function only triggers when the content field of a post document is initially published or updated, keeping automation efficient and intentional.
import {defineBlueprint, defineDocumentFunction} from '@sanity/blueprints'
export default defineBlueprint({
resources: [
defineDocumentFunction({
type: 'sanity.function.document',
src: './functions/auto-summary',
memory: 2,
timeout: 30,
name: 'auto-summary',
event: {
on: ['create', 'update'],
filter: "_type == 'post' && delta::changedAny(content)",
projection: '{_id}',
},
}),
],
})What makes this powerful is how easily it can scale. The same structure could be reused to generate SEO metadata, create social captions, tag posts by topic, or even translate content into multiple languages. You would simply adjust the instruction or target fields in the function, without needing new infrastructure or complex integrations. Once you define intent in a blueprint, extending it to new workflows becomes a matter of configuration, not code.
Liked this article? Share it with a friend on Bluesky or X. Have a question, feedback or simply wish to contact me privately? Shoot me a DM and I'll do my best to get back to you.