Hook System

Hooks let you run code at specific points in Dune's lifecycle — when a page loads, before rendering, after cache events, and more.

Available hooks

Startup hooks

Hook When it fires Use case
onConfigLoaded Config fully merged and validated Modify config, set up external services
onStorageReady Storage adapter initialized Verify connectivity, warm caches
onContentIndexReady Content index built/loaded Build search index, generate sitemap

Request lifecycle hooks

Hook When it fires Use case
onRequest Incoming request (before routing) Analytics, auth, rate limiting
onRouteResolved Route matched to a page URL rewriting, A/B testing
onPageLoaded Full page object loaded Content transformation
onCollectionResolved Collection query executed Modify collection results
onBeforeRender Before JSX rendering Inject data, modify props
onAfterRender After rendering (HTML available) Post-processing, minification
onResponse Before response sent Headers, compression

Content processing hooks

Hook When it fires Use case
onMarkdownProcess Before markdown → HTML Custom syntax, shortcodes
onMarkdownProcessed After markdown → HTML HTML post-processing
onMediaDiscovered Media files found for page Image optimization triggers

Cache hooks

Hook When it fires Use case
onCacheHit Serving from cache Analytics, cache headers
onCacheMiss Cache miss, will process Performance monitoring
onCacheInvalidate Cache entry invalidated CDN purging

API hooks

Hook When it fires Use case
onApiRequest Before API request is handled Auth, rate limiting, request logging
onApiResponse After API response is built Response transformation, headers

Engine lifecycle hooks

Hook When it fires Use case
onRebuild After a successful engine.rebuild() Clear downstream caches, notify search index
onThemeSwitch When the active theme changes Purge theme-specific caches, notify CDN

Content mutation hooks

Fired by the admin panel after CRUD operations. Useful for triggering external systems (CDN purges, search re-indexing, notifications) without using outbound webhooks.

Hook When it fires Use case
onPageCreate New page created and index rebuilt Notify external search, invalidate CDN
onPageUpdate Page saved and index rebuilt Notify external search, invalidate CDN
onPageDelete Page deleted and index rebuilt Remove from external search, purge CDN
onWorkflowChange Page workflow status changed Trigger review notifications, update CMS

Registering hooks

// plugins/my-hooks.ts
import type { DunePlugin } from "dune/types";

export default {
  name: "my-hooks",
  version: "1.0.0",
  hooks: {
    onRequest: async ({ data, config }) => {
      // Log every request
      console.log(`[${new Date().toISOString()}] ${data.req.method} ${data.req.url}`);
    },

    onMarkdownProcess: async ({ data, setData }) => {
      // Replace custom shortcodes before markdown processing
      const modified = data.raw.replace(
        /\{\{youtube\s+(\w+)\}\}/g,
        '<iframe src="https://youtube.com/embed/$1"></iframe>',
      );
      setData({ ...data, raw: modified });
    },

    onAfterRender: async ({ data }) => {
      // Add reading time to rendered HTML
      const wordCount = data.html.split(/\s+/).length;
      const minutes = Math.ceil(wordCount / 200);
      data.html = data.html.replace(
        "</article>",
        `<p class="reading-time">${minutes} min read</p></article>`,
      );
    },
  },
} satisfies DunePlugin;

Hook context

Every hook handler receives a HookContext object:

interface HookContext<T> {
  event: HookEvent;           // which hook is firing
  data: T;                    // event-specific data
  config: DuneConfig;         // full merged config
  storage: StorageAdapter;    // storage access
  stopPropagation(): void;    // stop further hooks for this event
  setData(data: T): void;     // replace event data
}

stopPropagation() prevents subsequent hooks from running for this event. Use it when a hook fully handles something (like a custom 404 page or an auth redirect).

setData() replaces the data flowing through the hook chain. The next hook receives the modified data.

Event data shapes

The data field in HookContext is typed per event. Here is what each hook receives:

Startup hooks (fired automatically by the engine)

Hook data type Description
onConfigLoaded DuneConfig The fully merged config object
onStorageReady StorageAdapter The initialized storage adapter
onContentIndexReady PageIndex[] All indexed pages

Request lifecycle hooks (intended for custom server integrations)

Hook data shape Description
onRequest { req: Request } Incoming HTTP request before routing
onRouteResolved { req: Request, page: PageIndex } Route matched to a page
onPageLoaded { req: Request, page: Page } Full page object ready
onCollectionResolved { req: Request, collection: Collection } Collection query result
onBeforeRender { req: Request, page: Page, props: Record<string, unknown> } Before JSX render
onAfterRender { req: Request, html: string } After render, HTML available
onResponse { req: Request, response: Response } Before response is sent

Content processing hooks

Hook data shape Description
onMarkdownProcess { raw: string, page: PageIndex } Raw Markdown before processing
onMarkdownProcessed { html: string, page: PageIndex } Rendered HTML after processing
onMediaDiscovered { media: MediaFile[], page: PageIndex } Media files found for a page

Cache hooks

Hook data shape Description
onCacheHit { key: string, value: unknown } Cache entry found
onCacheMiss { key: string } Cache entry not found
onCacheInvalidate { key: string } Cache entry removed

API hooks

Hook data shape Description
onApiRequest { req: Request } Before API request is handled
onApiResponse { req: Request, response: unknown } After API response is built

Engine lifecycle hooks

Hook data shape Description
onRebuild {} Fired at the end of a successful engine.rebuild()
onThemeSwitch { from: string, to: string } Fired when the active theme changes (old and new theme names)

Content mutation hooks

Hook data shape Description
onPageCreate { sourcePath: string, title: string } Fired after a new page is created via the admin panel
onPageUpdate { sourcePath: string, title: string } Fired after a page is saved via the admin panel
onPageDelete { sourcePath: string } Fired after a page is deleted via the admin panel
onWorkflowChange { sourcePath: string, from: WorkflowStatus, to: WorkflowStatus } Fired after a page's workflow status changes

WorkflowStatus is "draft" | "in_review" | "published" | "archived".

These same events also trigger outbound webhooks when admin.webhooks is configured — hooks and webhooks fire in parallel.

Note: The startup hooks (onConfigLoaded, onStorageReady, onContentIndexReady) and engine lifecycle hooks (onRebuild, onThemeSwitch) are fired automatically by Dune. The request and API hooks can also be fired by custom server code using hooks.fire(event, data) when integrating Dune into a custom server.

Hook execution order

Hooks fire in the order they're registered. If multiple plugins register the same hook, they run sequentially. Each hook sees the data as modified by previous hooks.