How to get first published and last published timestamps in Sanity?
You're absolutely right that _createdAt doesn't necessarily reflect when a document was first published—it just shows when the document was created in the system, which could have been while it was still a draft.
For tracking first published and last published dates, the recommended approach is to create custom fields and use a custom publish action to set them. Here's how:
The Solution: Custom Fields + Custom Publish Action
Add two fields to your schema:
{
name: 'firstPublished',
type: 'datetime',
title: 'First Published',
readOnly: true
},
{
name: 'lastPublished',
type: 'datetime',
title: 'Last Published',
readOnly: true
}Then create a custom publish action that sets firstPublished only on the first publish, and updates lastPublished every time:
import {useDocumentOperation} from 'sanity'
export function createPublishAction(originalPublishAction) {
return (props) => {
const {patch, publish} = useDocumentOperation(props.id, props.type)
const {draft, published} = props
return {
...originalPublishAction(props),
onHandle: () => {
// Set firstPublished only if document hasn't been published before
if (!published) {
patch.execute([{set: {firstPublished: new Date().toISOString()}}])
}
// Always update lastPublished
patch.execute([{set: {lastPublished: new Date().toISOString()}}])
// Perform the publish
publish.execute()
props.onComplete()
}
}
}
}This is actually documented as an example in Sanity's schema recipes!
Why Not Use the History API?
While you could parse the History API's NDJSON response to find the first transaction where a document was published (looking for transactions that created the document without the drafts. prefix), it's unnecessarily complex and has limitations based on your history retention period (3 days for Free, 90 days for Growth, 365 days for Enterprise).
The custom field approach is more reliable, performant, and gives you instant access to these timestamps in your queries without needing to process transaction history.
For Last Updated
For last published, you can actually use _updatedAt on the published document (not the draft), which will reflect when it was last published. Just make sure you're querying the published version specifically by filtering out drafts or using the published perspective in your queries.
Show original thread4 replies
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.