Plugin to create block-level custom containers for markdown-it markdown parser
npx @tessl/cli install tessl/npm-markdown-it-container@4.0.0markdown-it-container is a plugin for the markdown-it parser that enables creation of block-level custom containers using a fenced syntax similar to code blocks. It allows you to define custom container types (like warnings, notes, or spoilers) with configurable validation, rendering, and marker characters.
npm install markdown-it-containerimport container from "markdown-it-container";For CommonJS:
const container = require("markdown-it-container");import markdownit from "markdown-it";
import container from "markdown-it-container";
// Simple container with default rendering
const md = markdownit().use(container, "warning");
const result = md.render(`
::: warning
*Here be dragons*
:::
`);
// Output: <div class="warning"><p><em>Here be dragons</em></p></div>
// Custom container with custom renderer
const mdCustom = markdownit().use(container, "spoiler", {
render: function (tokens, idx) {
if (tokens[idx].nesting === 1) {
// opening tag
return '<details><summary>Click to reveal</summary>\n';
} else {
// closing tag
return '</details>\n';
}
}
});The main plugin function that registers a custom container with markdown-it.
/**
* Registers a custom container with markdown-it parser
* @param md - The markdown-it parser instance
* @param name - Container name (used as CSS class and validation)
* @param options - Configuration options for the container
*/
export default function container(md: MarkdownIt, name: string, options?: ContainerOptions): void;
interface ContainerOptions {
/** Custom validation function for container parameters */
validate?: (params: string, markup: string) => boolean;
/** Custom renderer for opening/closing tokens */
render?: (tokens: Token[], idx: number, options: any, env: any, renderer: any) => string;
/** Character(s) to use as delimiter (default: ':') */
marker?: string;
}Parameters:
md (MarkdownIt): The markdown-it parser instance to register the container withname (string): Container name - used as CSS class in default rendering and for validationoptions (ContainerOptions, optional): Configuration optionsOptions Properties:
validate (function, optional): Function to validate container parameters
params (string) - text after opening marker, markup (string) - full opening markertrue if valid, false/falsy to skip processing::: warning message is valid, but ::: warn message is notrender (function, optional): Custom renderer for opening/closing tokens
tokens (Token[]) - Array of all tokensidx (number) - Index of current token being renderedoptions (any) - Markdown-it optionsenv (any) - Environment objectrenderer (any) - The markdown-it renderer instance<div> with container name as CSS class using renderer.renderToken()marker (string, optional): Character(s) used as delimiter (default: ':')Usage Examples:
// Basic container with default div rendering
md.use(container, "note");
// ::: note
// content
// :::
// → <div class="note"><p>content</p></div>
// Container with custom marker
md.use(container, "custom", {
marker: ">"
});
// >>> custom
// content
// >>>
// → <div class="custom"><p>content</p></div>
// Container with custom validation
md.use(container, "alert", {
validate: function(params) {
return params.trim().match(/^alert\s+(info|warning|error)$/);
}
});
// Only accepts: ::: alert info, ::: alert warning, ::: alert error
// Container with custom rendering
md.use(container, "details", {
validate: function(params) {
return params.trim().match(/^details\s+(.*)$/);
},
render: function(tokens, idx) {
if (tokens[idx].nesting === 1) {
return '<details><summary>Click to reveal</summary>\n';
} else {
return '</details>\n';
}
}
});The plugin enables the following markdown syntax:
::: container_name [optional parameters]
Content goes here (parsed as markdown)
:::Syntax Rules:
:::, ::::, :::::)Examples:
<!-- Basic container -->
::: warning
This is a warning message
:::
<!-- Container with parameters -->
::: alert info Important Notice
Check the documentation for details
:::
<!-- Nested containers -->
::::: outer
:::: inner
Content here
::::
:::::
<!-- Longer closing marker -->
::: container
Content
::::::::::interface MarkdownIt {
use(plugin: Function, ...args: any[]): MarkdownIt;
render(src: string, env?: any): string;
block: {
ruler: {
before(beforeName: string, ruleName: string, rule: Function, options?: any): void;
};
tokenize(state: any, start: number, end: number): void;
};
renderer: {
rules: Record<string, Function>;
};
utils: {
escapeHtml(str: string): string;
};
}
interface Token {
/** Token type (e.g., 'container_name_open', 'container_name_close') */
type: string;
/** Nesting level: 1 for opening, -1 for closing, 0 for self-closing */
nesting: number;
/** Content after container name (parameters/arguments) */
info: string;
/** The marker string used to open/close (e.g., '::::', ':::::') */
markup: string;
/** Whether this is a block-level token */
block: boolean;
/** Source line mapping [start_line, end_line] */
map: [number, number] | null;
/** Add or join CSS class to token attributes */
attrJoin(name: string, value: string): void;
}