JustFlows

Hooks

Typed actions, gates, and filters — the complete core hook list and how to register them.

10 min read

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 happenedactioncontent.published
Stop something before it commitsgatecontent.beforeCreate
Change a value on the way throughfiltercontent.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.

ActionWhen
app.starting / app.started / app.stoppingProcess lifecycle
content.created / updated / deleted / published / unpublishedContent
media.uploaded / media.deletedMedia
user.created / updated / deletedUsers (needs users:read)
auth.login / logout / loginFailedAuth (needs auth:hook)
plugin.installed / activated / deactivated / uninstalledPlugins
theme.installed / theme.activatedThemes
request.before / request.afterHTTP
site.underConstruction.viewedUnpublished site hit
cache.revalidatedAfter selective cache revalidate

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 / beforePublish
  • media.beforeUpload / media.beforeDelete
ts
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.

FilterTransforms
content.input / content.outputContent records in/out
content.renderHTML string (must be sync)
media.metadataMetadata object
navigation.itemsMenu tree
http.responseHeadersHeader map (must be sync)
site.underConstruction.renderUnder-construction HTML (must be sync)

SYNC_FILTERS: content.render, http.responseHeaders, site.underConstruction.render — handlers must not 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 / FilterValueMap types your own names.

Note

Longer narrative and cleanup rules: docs/HOOKS.md in justflows-platform-ce.