Docusaurus Custom Plugin Development
Docusaurus plugins are functions that extend build lifecycle, add new pages, modify webpack config, or inject data from external sources.
Plugin Structure
// plugins/my-plugin/index.ts
import type { LoadContext, Plugin } from '@docusaurus/types';
interface PluginOptions {
apiUrl: string;
cacheTime?: number;
}
export default function myPlugin(
context: LoadContext,
options: PluginOptions
): Plugin<{ apiData: any[] }> {
return {
name: 'my-docusaurus-plugin',
// Loading data from external source
async loadContent() {
const res = await fetch(options.apiUrl);
return { apiData: await res.json() };
},
// Creating pages based on data
async contentLoaded({ content, actions }) {
const { createData, addRoute } = actions;
for (const item of content.apiData) {
const dataPath = await createData(
`api-item-${item.id}.json`,
JSON.stringify(item)
);
addRoute({
path: `/api-reference/${item.slug}`,
component: '@site/src/components/ApiItemPage',
modules: { apiData: dataPath },
exact: true,
});
}
// Index page
const allDataPath = await createData('all-api-items.json', JSON.stringify(content.apiData));
addRoute({
path: '/api-reference',
component: '@site/src/components/ApiIndexPage',
modules: { allItems: allDataPath },
exact: true,
});
},
// Webpack config modification
configureWebpack(config, isServer) {
return {
module: {
rules: [
{
test: /\.ya?ml$/,
use: 'yaml-loader',
},
],
},
};
},
// HTML injection into <head> of all pages
injectHtmlTags() {
return {
headTags: [
{
tagName: 'script',
attributes: {
defer: true,
src: 'https://analytics.mysite.com/script.js',
'data-site': 'MYSITE_ID',
},
},
],
};
},
};
}
// Options validation
export function validateOptions({ validate, options }) {
const ValidatedOptions = validate({
apiUrl: Joi.string().uri().required(),
cacheTime: Joi.number().default(3600),
}, options);
return ValidatedOptions;
}
Plugin Registration
// docusaurus.config.ts
plugins: [
[
'./plugins/my-plugin',
{
apiUrl: 'https://api.myproject.com/endpoints',
cacheTime: 7200,
},
],
// Local plugin without options
'./plugins/generate-sitemap-extras',
],
Plugin for Loading Changelog from GitHub
// plugins/github-changelog/index.ts
import { Octokit } from '@octokit/rest';
export default function githubChangelogPlugin(context, options) {
return {
name: 'github-changelog-plugin',
async loadContent() {
const octokit = new Octokit({ auth: options.token });
const { data: releases } = await octokit.repos.listReleases({
owner: options.owner,
repo: options.repo,
per_page: 50,
});
return { releases };
},
async contentLoaded({ content, actions }) {
const { createData, addRoute } = actions;
const dataPath = await createData(
'releases.json',
JSON.stringify(content.releases)
);
addRoute({
path: '/changelog',
component: '@site/src/pages/Changelog',
modules: { releases: dataPath },
exact: true,
});
},
};
}
Remark/Rehype Plugins for MDX
// plugins/remark-custom-directives.ts
import type { Plugin } from 'unified';
import { visit } from 'unist-util-visit';
const remarkCustomDirectives: Plugin = () => (tree) => {
visit(tree, 'containerDirective', (node) => {
if (node.name === 'api-endpoint') {
// Transform custom directive into JSX
node.data = {
hName: 'div',
hProperties: { className: ['api-endpoint', `method-${node.attributes?.method}`] },
};
}
});
};
export default remarkCustomDirectives;
// docusaurus.config.ts
presets: [['classic', {
docs: {
remarkPlugins: [
require('./plugins/remark-custom-directives'),
require('remark-math'),
],
rehypePlugins: [require('rehype-katex')],
},
}]],
Plugin development for loading external data and creating pages — 2–4 days.







