examples/functions/auto-changelog/README.md
Returning readers can see when an article was last updated, but not what was changed. Meanwhile, content editors need to manually document meaningful changes for these readers, but this process is time-consuming and often forgotten during busy editing workflows. Teams want to show content evolution to readers while avoiding noise from minor formatting tweaks.
This Sanity Function automatically generates changelog entries when documents are updated with meaningful changes. It uses AI to analyze content changes, filtering out minor modifications, and generates concise, reader-friendly descriptions of what actually changed. Fine-grained changes for all fields can still be track with Sanity's out of the box History.
For readers:
For content editors:
This function is built to be compatible with any of the official "clean" templates. We recommend testing the function out in one of those after you have installed them locally.
post document type containing:
content field (rich text/portable text) for content analysischangelog field (array of strings) for storing generated entriesEditor workflow: When a content editor updates a blog post with meaningful changes, they simply publish their changes as usual - no extra changelog work required.
Behind the scenes, the function automatically:
Reader outcome: Visitors see clear change tracking like "Clarified explanation of the model's limitations; Added more context about the implementation process" without noise from minor wording tweaks, helping them understand exactly what's new since their last visit.
You'll need to add both a changelog field and properly configured block content to your post schema:
studio/src/schemaTypes/documents/post.ts)fields array:defineField({
name: "changelog",
title: "Changelog",
type: "array",
of: [
defineArrayMember({
type: "string",
}),
],
description: "List of changes made so returning readers can see how it has progressed.",
}),
content field with basic block content:defineField({
name: 'content',
title: 'Content',
type: 'array',
of: [
defineArrayMember({
type: 'block',
}),
],
}),
# From the studio/ folder
npx sanity schema deploy
Important: Run these commands from the root of your project (not inside the studio/ folder).
Initialize the example
Run this if you haven't initialized blueprints:
npx sanity blueprints init
You'll be prompted to select your organization and Sanity studio.
Then run:
npx sanity blueprints add function --example auto-changelog
Add configuration to your blueprint
Add the following resource to your sanity.blueprint.ts:
defineDocumentFunction({
src: './functions/auto-changelog',
memory: 2,
timeout: 30,
name: 'auto-changelog',
event: {
on: ['update'],
filter: "_type == 'post' && delta::changedAny(content)",
projection:
"{_id, 'oldContent': pt::text(before().content), 'newContent': pt::text(after().content), changelog}",
},
})
Install dependencies
Install dependencies in the project root:
npm install
Make sure you have a schema deployed
From the studio folder, run:
# In the studio/ folder
npx sanity schema deploy
The auto-changelog function uses delta GROQ operations (delta::changedAny(), before(), after()) to detect changes. To test this locally with the CLI, you need to provide both before and after versions of a document. This works well by making a draft of a document, and then using the documentId and the draft documentId:
npx sanity functions test auto-changelog --event update \
--document-id-before <post-id> \
--document-id-after draft:<post-id> \
--dataset production \
--with-user-token
For a more interactive testing experience, use the development server:
npx sanity functions dev
This opens a web UI where you can test the "before" and "after" data
For advanced users who need automatic changelog tracking for code blocks within Portable Text, see this Gist with all three components:
This adds:
To track when changes were made, you can add a datetime field to store timestamps alongside changelog entries. This requires modifying both your schema and the function:
defineField({
name: "changeHistory",
title: "Change History",
type: "array",
of: [
defineArrayMember({
type: "object",
fields: [
defineField({
name: "change",
title: "Change Description",
type: "string",
}),
defineField({
name: "timestamp",
title: "Change Date",
type: "datetime",
}),
],
}),
],
description: "List of changes with timestamps for tracking when modifications were made.",
}),
Customize the changelog generation style by editing the instruction string in index.ts:
instruction: `
Generate changelog entries focusing on:
- User-facing impacts for content changes
- Concise, professional tone
// ... your custom instructions
`
For longer posts with complex changes, you may need to increase the timeout:
timeout: 120, // Increase from 90 to 120 seconds