Plugin System
Krate's plugin system is a single, unified interface with 7 lifecycle hooks. Built-in plugins are written in Go; community plugins are JavaScript modules executed inside the embedded QuickJS runtime — no subprocess, no stdin/stdout protocol.
The plugin interface
type Plugin interface {
Name() string
Order() int // Lower runs first (default: 50)
Hooks() PluginHooks
}
type PluginHooks struct {
BeforeBuild func(ctx *BuildHookCtx) error
AfterParse func(ctx *ParseHookCtx) error
AfterMarkdownParse func(ctx *MarkdownHookCtx) error
AfterRender func(ctx *RenderHookCtx) error
GenerateRoutes func(ctx *BuildHookCtx) ([]Route, error)
AfterPage func(ctx *PageHookCtx) error
AfterBuild func(ctx *BuildResultHookCtx) error
} The 7 lifecycle hooks
| Hook | When | Mutable Context |
|---|---|---|
BeforeBuild | Before any pages are built | Root, Config |
AfterParse | After a page is parsed (AST available) | AST, Source |
AfterMarkdownParse | After markdown/MDX is parsed | HTML, Frontmatter |
AfterRender | After SSR rendering | HTML, HeadHTML, Signals, Handlers |
GenerateRoutes | Generate virtual pages | Routes |
AfterPage | After a page is fully built | HTML, Route |
AfterBuild | After all pages are built | Results, Manifest |
Config usage (typed)
import { defineConfig, sitemap, docs } from '@krate/core';
import demoPlugin from './plugins/krate-plugin-demo';
export default defineConfig({
plugins: [
sitemap({ baseUrl: "https://example.com" }),
docs({ contentDir: "content/docs", title: "Docs" }),
demoPlugin({ greeting: "Hello!" }),
],
}); Each factory returns a serializable descriptor: { name, order, options } (built-ins) or { name, order, module, options } (community plugins).
Community plugin protocol
Community plugins are JavaScript modules executed inside the embedded QuickJS runtime (modernc.org/quickjs). The module is bundled with esbuild into a self-contained IIFE and its hooks are called directly from Go.
// plugins/my-plugin/index.js
export const hooks = {
BeforeBuild(ctx, options, krate) {
return { files: [{ path: "note.txt", content: "hi" }] };
},
AfterRender(ctx, options, krate) {
return {
html: "<b>" + ctx.html,
headHTML: "<meta ...>",
rawCSS: ".x{}",
};
},
};
export default function myPlugin(options) {
return {
name: "my-plugin",
order: 20,
module: typeof import.meta !== "undefined" && import.meta.url ? import.meta.url : "",
options: options || {},
};
} - Hook signature — every hook receives
(ctx, options, krate)wherectxis the JSON-serialized hook context (lowercase fields likectx.html,ctx.page,ctx.outName,ctx.headHTML,ctx.rawCSS) andkrateis{ root, outDir, version }. - Return value — hooks return
{ files, routes, generatedPages, html, headHTML, rawCSS }(all optional; may be a Promise).filesare written into the output directory (path traversal is rejected),routesbecome static HTML pages,generatedPagesfeed the page pipeline, andhtml/headHTML/rawCSSmutate the hook context. - Runtime capabilities — bundled plugins can use
import fs from 'fs'/import path from 'path'(polyfilled) plus Web API polyfills (fetch,URL,Headers,Response,TextEncoder, timers,process.env). Non-relative third-party imports are left external and unavailable.
Built-in plugins
| Plugin | Purpose |
|---|---|
sitemap | Generates sitemap.xml |
icons | <Icon> → Iconify SVG with disk cache |
imageprocessing | <Image> → responsive <picture> with srcset |
markdown | Markdown/MDX compilation |
csp | Content Security Policy meta tag |
docs | Documentation site generator with WASM search |
See Guides: Create a Plugin and the demo plugin in the examples for a full walkthrough.