TidyPress extensions are project-local modules compiled into a generated
manifest during dev and build. Paths stay in the repository and are validated
before the engine imports them.
Presentation plugin
import { definePlugin } from 'tidypress'
export default definePlugin({
name: 'brand-system',
setup(context) {
context.addStyle('./src/styles/brand.css')
context.addMdxComponent('Metric', './src/components/Metric.astro')
},
}) addMdxComponent(name, component) method addSlot(slot, component) method addThemeComponent(name, component, options?) method addLayout(name, component) method layout.addStyle(path) method addScript(path) method addRemarkPlugin(path) method addRehypePlugin(path) method addSearchProvider(path) method UI component boundaries
ui.components and addThemeComponent accept these stable names:
RouteView · RouteViewShell
Document · DocumentHead · Analytics · ThemeBoot · BodyScripts · RootShell
Navbar · Footer · Home
DocLayout · DocHeader · DocSidebar · Toc
Search · CodeBlock · PageLayout · NotFound String values replace the original component. Object values accept
{ component, mode: 'replace' | 'wrap' }. Overrides receive their component
props plus Original; wrappers receive the rendered original in their slot.
Collection presentation
import type { TidyPressPluginPresentation } from '@tidypress/engine/plugins'
export function createPresentation(site, { collectionKey }): TidyPressPluginPresentation {
return {
async buildIndex(route) {
return { viewKey: `${collectionKey}:collection-index`, site, route, title: 'API', headings: [], pagefindIgnore: false }
},
async buildEntry(route) {
return { viewKey: `${collectionKey}:collection-entry`, site, route, title: route.slug ?? 'Entry', headings: [], pagefindIgnore: false }
},
}
} buildIndex(route) async method required buildEntry(route) async method required Set the module through collections.<key>.render.presentation. Optional Astro
views live in the directory from render.views and use the filenames
collection-index.astro, collection-entry.astro, and version-root.astro.
Missing optional files use the collection kind’s baseline shell. A registered
file that cannot load is a build error rather than a silent fallback.
Docs forms
Custom form keys are registered under extensions.docForms. Each descriptor can
provide label, presentation, and views. The page selects it with
frontmatter form: your-key. Built-in doc and manual names are reserved.
Search provider
import { defineSearchProvider } from 'tidypress'
export default defineSearchProvider({
async search(query, options) {
return [{ url: '/docs/result', title: `Result for ${query}`, collection: options?.collection }]
},
}) search(query, options?) async method required Return { url, title, excerpt?, collection? }[]. The module runs in the
browser, so do not embed private credentials.
Reload and failure behavior
During development, config and manifest changes advance the manifest epoch and reload registered modules. Invalid paths, missing required exports, unsafe parent traversal, and broken registered views fail with actionable errors.