Creating Plugins
Build your own tulip plugin with typed TypeScript contracts and lifecycle hooks.
Scaffold a plugin
Use the CLI to generate a new plugin:
tulip plugin new seoThis creates the following structure in plugins/seo/:
plugins/seo/
plugin.toml # metadata and default config
index.ts # entry point
types.d.ts # TypeScript definitions
README.md # documentationPlugin manifest
The plugin.toml file declares your plugin metadata and default configuration:
[plugin]
name = "seo"
version = "0.1.0"
description = "Adds SEO meta tags to every page"
author = "Your Name"
[config]
site_name = "My Site"
twitter_handle = ""
og_image = "/images/og-default.png"| Field | Required | Description |
|---|---|---|
name | Yes | Plugin identifier (must match directory name) |
version | Yes | Semantic version |
description | Yes | Short description shown in tulip plugin list |
author | No | Plugin author |
The [config] section defines default values. Users override these in tulip.toml.
Entry point
The index.ts file exports a TulipPlugin object. Implement only the hooks you need:
const plugin: TulipPlugin = {
name: "seo",
head(): HeadTag[] {
return [
{ tag: "meta", attrs: { property: "og:site_name", content: this.config.site_name } },
];
},
transformHtml(html: string, page: Page): string {
return html;
},
};
export default plugin;Type definitions
The types.d.ts file provides the TypeScript contract for your plugin:
interface TulipPlugin {
name: string;
setup?(config: PluginConfig): void;
beforeBuild?(ctx: BuildContext): void;
transformMarkdown?(content: string, page: Page): string;
transformContext?(ctx: object, page: Page): object;
transformHtml?(html: string, page: Page): string;
head?(): HeadTag[];
generatePages?(ctx: BuildContext): GeneratedPage[];
afterBuild?(ctx: BuildContext): void;
}Full example: SEO plugin
A complete plugin that injects Open Graph meta tags using head() and transformHtml():
// plugins/seo/index.ts
const plugin: TulipPlugin = {
name: "seo",
head(): HeadTag[] {
return [
{ tag: "meta", attrs: { property: "og:site_name", content: this.config.site_name } },
{ tag: "meta", attrs: { property: "og:type", content: "website" } },
];
},
transformHtml(html: string, page: Page): string {
let title = page.frontmatter.title || this.config.site_name || "";
let description = page.frontmatter.description || "";
let image = page.frontmatter.og_image || this.config.og_image || "";
let tags = '<meta property="og:title" content="' + title + '">\n';
tags += '<meta property="og:description" content="' + description + '">\n';
if (image) {
tags += '<meta property="og:image" content="' + image + '">\n';
}
if (this.config.twitter_handle) {
tags += '<meta name="twitter:card" content="summary_large_image">\n';
tags += '<meta name="twitter:site" content="' + this.config.twitter_handle + '">\n';
}
return html.replace("</head>", tags + "</head>");
},
};
export default plugin;Register the plugin
Add the plugin to tulip.toml to activate it:
[[plugins]]
name = "seo"
config = { site_name = "My Blog", base_url = "https://blog.example.com" }