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 seo

This 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       # documentation

Plugin 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"
FieldRequiredDescription
nameYesPlugin identifier (must match directory name)
versionYesSemantic version
descriptionYesShort description shown in tulip plugin list
authorNoPlugin 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" }