Skip to content

Hooks

Define hooks directly in your collection config. They run inside the local API on every operation.

src/cms/collections/posts.ts
import { defineCollection, fields, richTextToPlainText } from "@kidecms/core";
export default defineCollection({
slug: "posts",
labels: { singular: "Post", plural: "Posts" },
fields: {
title: fields.text({ required: true }),
body: fields.richText(),
excerpt: fields.text({ maxLength: 300 }),
},
hooks: {
beforeCreate(data, context) {
// Auto-generate excerpt from body
if (!data.excerpt && typeof data.body === "object") {
data.excerpt = richTextToPlainText(data.body).slice(0, 180);
}
return data;
},
afterPublish(doc, context) {
context.cache?.invalidate({ tags: ["posts", `post:${doc._id}`] });
},
afterDelete(doc, context) {
context.cache?.invalidate({ tags: ["posts", `post:${doc._id}`] });
},
},
});
Hook Arguments Return
beforeCreate (data, context) Modified data
afterCreate (doc, context)
beforeUpdate (data, existing, context) Modified data
afterUpdate (doc, context)
beforeDelete (doc, context)
afterDelete (doc, context)
beforePublish (doc, context)
afterPublish (doc, context)
beforeUnpublish (doc, context)
afterUnpublish (doc, context)
beforeSchedule (doc, context)
afterSchedule (doc, context)
beforeUpsertTranslation (data, locale, existing, context) Modified data

before* hooks on create/update receive the data and can modify it before saving. All other before* hooks can throw to abort the operation. after* hooks are for side effects like cache invalidation.

beforeUpsertTranslation is the only hook that fires on upsertTranslationbeforeCreate and beforeUpdate do not fire there.

{
user: { id, role, email } | null,
operation: "create" | "update" | "delete" | ...,
collection: "posts",
timestamp: "2025-01-01T00:00:00Z",
cache: {
invalidate: ({ tags: string[] }) => void
}
}

If your public pages use Astro’s route caching (see Public Pages), invalidating tags when content changes is something you write in hooks like this — it does not happen on its own. Use context.cache.invalidate() in after* hooks:

afterPublish(doc, context) {
context.cache?.invalidate({
tags: ["posts", `post:${doc._id}`]
});
},

Tag your Astro pages to match:

---
Astro.cache.set({ tags: ["posts", `post:${post._id}`] });
---