Документация пока доступна только на английском языке. Остальная часть сайта соответствует вашему языку.
Hooks
Typed actions, gates, and filters — the complete core hook list and how to register them.
10 минута чтения
Hooks are how you change what Justflows does without changing Justflows. Write the plugin in plugins/<name>/ (copy plugins/hello-world) and register hooks in activate(). Names autocomplete from @justflows/sdk.
| I want to… | Use a… | Names look like |
|---|---|---|
| React after something happened | action | content.published |
| Stop something before it commits | gate | content.beforeCreate |
| Change a value on the way through | filter | content.render |
Actions
Observe only. Cannot cancel. If your handler throws, Justflows logs it against your plugin and continues — a broken analytics plugin must not block publishing. Treat payloads as read-only. Async handlers are awaited in order.
| Action | When |
|---|---|
app.starting / app.started / app.stopping | Process lifecycle |
content.created / updated / deleted / published / unpublished | Content |
media.uploaded / media.deleted | Media |
user.created / updated / deleted | Users (needs users:read) |
auth.login / logout / loginFailed | Auth (needs auth:hook) |
plugin.installed / activated / deactivated / uninstalled | Plugins |
theme.installed / theme.activated | Themes |
request.before / request.after | HTTP |
site.underConstruction.viewed | Unpublished site hit |
cache.revalidated | After selective cache revalidate |
webhook.delivery | After each outgoing webhook attempt (status, response, error) |
staticExport.completed / staticExport.deploy | A static / edge export run finished; deploy fires right after so a plugin can push the directory to a CDN |
Gates
Run before commit. Call event.cancel("Human-readable reason") to abort. Fail closed: if your gate throws, the operation is aborted and attributed to your plugin. Priority order; first cancellation wins.
content.beforeCreate/beforeUpdate/beforeDelete/beforePublishmedia.beforeUpload/media.beforeDelete
ctx.hooks.gate("media.beforeUpload", (event) => {
if (event.sizeBytes > 10_000_000) {
event.cancel("Files must be under 10 MB.");
}
});Filters
You must return a value. On core hooks TypeScript enforces it. If a custom filter returns undefined, the previous value is kept and a warning is logged. Throws skip your filter and keep the last good value.
| Filter | Transforms |
|---|---|
content.input / content.output | Content records in/out |
content.render | Public HTML string (may be async) |
media.metadata | Metadata object |
navigation.items | Menu tree (runs after the host resolves a menu, visibility rules applied) |
menu.design.presets | One-click menu layout presets a plugin/theme ships (static data; ids "<pluginId>:<slug>") |
menu.visibility.evaluate | Answer an item-level custom visibility condition your plugin owns (seeded false, fail-closed) |
http.responseHeaders | Header map (must be sync) |
html.head | Extra <head> HTML (must be sync) |
theme.css | Plugin CSS appended to cached /theme.css (may be async) |
seo.sitemapPaths | Array of sitemap path strings |
site.underConstruction.render | Under-construction HTML (must be sync) |
comments.render | Rendered public comments block HTML (may be async) |
header.templates / header.resolve / header.config | Contribute named header designs, own a page's header per request, or adjust the resolved HeaderConfig before render |
webhook.eventTypes | Array of deliverable webhook event names |
webhook.payload | Webhook JSON body before it is signed and sent |
staticExport.routes / staticExport.assets | Seed paths crawled by the exporter, and same-origin asset URLs the scanner cannot discover |
staticExport.formAction | Override the <form action> written into exported HTML for a dynamic endpoint |
SYNC_FILTERS: http.responseHeaders, html.head, site.underConstruction.render — handlers must not be async. content.blocks, content.render, and theme.css may be async.
Options and ownership
priority— lower runs earlier (default 100).once— auto-dispose after first dispatch.id— stable label in diagnostics.- Plugins may emit only hooks under their own manifest id (
acme.seo.scoreCalculated). - Declaration merging on
ActionEventMap/GateEventMap/FilterValueMaptypes your own names.
Header and comment hooks
header.templates contributes named headers that appear in the per-page picker, build()-rendered at request time. header.resolve lets a plugin own a page's header per request; header.config adjusts the resolved header before render. comments.render restyles or replaces the rendered comments block. New SDK types: HeaderConfig, HeaderTemplate, HeaderBuildContext, HeaderResolveContext, CommentsBlockRenderContext, PublicComment. Outgoing webhooks add two filters — webhook.eventTypes to register event names and webhook.payload to reshape a body — plus the webhook.delivery action after every attempt; see Webhooks. Access-policy changes emit access-change hooks; a plugin registers a runtime capability with ctx.capabilities.register() and declares cookies with ctx.cookies.declare() (reading the resolved site registry with ctx.cookies.list()). The synchronous analytics.head filter lets Cookie consent defer third-party tags until consent is granted. For menus, menu.design.presets contributes one-click layout presets to the designer's design panel and menu.visibility.evaluate resolves a plugin-defined item condition — an unrecognized or deactivated condition id hides the item. See docs/HOOKS.md → Contributing a menu design preset in the Community Edition repository.
Note
Longer narrative and cleanup rules: docs/HOOKS.md in the Community Edition repository.