Plugin Recipes
Writing a Plugin builds one plugin from an empty folder to a working directive. This page is the next step: a set of small recipes for the extension points you are likely to need.
Each recipe is independent. Copy the one you need into extensions/, register it in riebeckite.config.ts, and check the result in the browser or build output. The recipes are task-oriented; the exact contracts are in the Plugin API.
Plugins are trusted application code. When a recipe builds HTML, it escapes untrusted values. When it emits URLs, it validates them for the context. Riebeckite does not sanitize plugin-generated HTML. See the Security model.
Before you start
The recipes assume a site created by create-riebeckite and a local plugin in extensions/. A local plugin does not need its own package.
Start with the smallest possible plugin:
import { definePlugin } from "@riebeckite/core";
export function example() {
return definePlugin({
name: "example",
});
}
Register it:
import { defineConfig } from "@riebeckite/core";
import { example } from "./extensions/example";
export default defineConfig({
plugins: [example()],
});
Each recipe defines its own factory. Put it in extensions/ and register it instead of example. The name must be unique across enabled plugins, because it owns the plugin cache and generated output namespace.
Recipe map
| What you need | Extension point | Recipe |
|---|---|---|
| Transform Markdown or HTML | remarkPlugins, rehypePlugins, pipeline hooks |
1 |
| Render an embedded file | renderers |
2 |
| Read content during the build | onConfigResolved, onContentLoaded, onPostParsed, onPostProcessed, onManifestCreated |
3 |
| Change public URLs | resolveContentLocations, extendContentLocations |
4 |
| Add links for the graph and backlinks | extendContentGraph |
5 |
| Add a standalone page | pageTypes |
6 |
| Share a UI component | package export | 7 |
| Add CSS and browser behavior | assets, clientEntries |
8 |
| Expose JSON or another HTTP response | endpoints |
9 |
| Change titles, feeds, or structured data | seo |
10 |
| Report content problems | addDiagnostics, inspectGeneratedHtml |
11 |
| Reuse derived data across builds | cacheVersion, context.cache |
12 |
| Write files during the build | planGeneratedOutputs, context.output |
13 |
| Control order and validate options | provides, requires, optional, order, validateOptions |
14 |
| Inspect build behavior | logger, tracer |
15 |
Check each recipe
| Recipe | What to check |
|---|---|
| Transform | The expected markup appears in the generated HTML |
| Renderer | ![[data.csv]] shows the preview; a plain link still works |
| Content hooks | The CLI prints the diagnostic with the plugin name |
| Locations | The new URL works and the old URL redirects |
| Graph | The related note appears as a backlink and graph neighbor |
| Page type | /series/typescript is generated and lists the notes |
| Component | The site imports it and renders it where it belongs |
| CSS and client | The stylesheet loads and the button works after the page loads |
| Endpoint | The dev server returns the expected JSON and cache header |
| SEO | The page source, sitemap, and feeds use the new metadata |
| Diagnostics | Warnings appear for the notes you expect |
| Cache | The second build reuses the cache; a content change misses it |
| Generated files | The file exists in the output with the expected content |
| Dependencies | A missing provider fails with a clear error |
| Logging | Debug messages appear only at the debug level |
Run npm run build for recipes that affect static output. For browser behavior, use npm run dev and check the console and network panel.
The testing levels in Writing a Plugin apply to every recipe. For framework-level test conventions, see Testing.
Common mistakes
- Reading
manifest.entriesfor published output. It includes drafts. UsepublicEntriesordiscoverableEntries. - Returning an empty string from a renderer for an unsupported target. Return
nullso another renderer can handle it. - Forgetting to escape values from frontmatter, files, or remote responses.
- Passing secrets through
publicConfig. Only that value is sent to the browser. - Adding a transform without
processedContentCache. The site then cannot reuse processed content. - Assuming an endpoint becomes a static file. Use generated outputs for static export.
- Emitting a planned output without dependencies. The runtime rejects it.
- Changing a permalink without a redirect. Old links break; add a redirect if the URL was public.
- Using
orderto override a capability dependency. Providers always run first.