Color mode

Shortcodes

A generic shortcode system for Markdown, built on remark-directive.

日本語

Overview

shortcodes() turns remark-directive syntax into HTML at Markdown time. It supports inline shortcodes (:name[label]{key=value}), block leaf shortcodes (::name[label]{key=value}), and container shortcodes (:::name[label]{attrs} … :::), and ships an extensible registry so projects can add their own renderers on top of the built-ins.

Inline shortcodes render as a span inside the surrounding paragraph; block and container shortcodes render as a div. Every wrapper carries rb-shortcode rb-shortcode--<name>.

Usage

ts
import { defineConfig } from "@riebeckite/core";
import { shortcodes } from "@riebeckite/plugin-shortcodes";
 
export default defineConfig({
  // ...
  plugins: [shortcodes()],
});

Styles ship in style.css.

Syntax

text
:badge[New]{variant=success}
 
::kbd[Ctrl+S]
 
::youtube[id=dQw4w9WgXcQ]
 
:::note[Heads up]{type=warning}
Container bodies support **Markdown**.
:::
  • :name[label]{key=value} — inline shortcode, rendered as a span inside the current paragraph. Only names in inlineShortcodes can be used inline; built-ins opt in with badge, kbd, link-card, and file.
  • ::name[label]{key=value} — block leaf shortcode, rendered as a div.
  • :::name[label]{key=value} … ::: — container shortcode; the body is rendered by the normal Markdown pipeline.

A shortcode that is only defined as a block emits a shortcodes-inline-unsupported diagnostic when written with : and stays on the page as escaped text. Unknown shortcodes emit a shortcodes-unknown diagnostic, and malformed attributes emit shortcodes-invalid and are ignored.

Built-in shortcodes

Name Kind Inline Attributes Output
figure leaf / container – src/url/image, alt, caption, width, height <figure> with image and caption
youtube leaf – id/video or url/src, title Privacy-friendly youtube-nocookie.com embed
vimeo leaf – id/video or url/src, title player.vimeo.com embed (dnt=1)
gist leaf – user + id, or url, file GitHub Gist embed with <noscript> link
kbd leaf yes label or keys <kbd> elements split on +
badge leaf yes label or text, variant/type/color, title Inline badge
details container – label or summary, open <details> disclosure
spoiler container – label or summary, open details alias with a different class
note leaf / container – label or title, type/variant Callout box
callout leaf / container – label or title, type/variant note alias with a different class
link-card leaf yes url/href, title, description, image/icon Link preview card
file leaf yes url/src/path, name/label, size Download link

Options

Option Type Default Description
className string "rb-shortcode" Root CSS class of every wrapper
language string – BCP-47 tag applied to wrappers as lang
builtins boolean true Register the built-in renderers
shortcodes Record<string, ShortcodeRenderer> {} Custom renderers, merged over the built-ins
inlineShortcodes readonly string[] [] Extra names allowed in the inline : form

Custom renderers

ts
import { shortcodes } from "@riebeckite/plugin-shortcodes";
 
shortcodes({
  shortcodes: {
    mark: ({ label, attributes }) => `<mark>${label}</mark>`,
  },
  inlineShortcodes: ["mark"],
});

A ShortcodeRenderer receives { name, label, attributes, childrenHtml, context, container } and returns a string. A custom renderer can be used with the inline : form only when its name is listed in inlineShortcodes. For container shortcodes the plugin wraps the result in <div class="rb-shortcode rb-shortcode--<name>"> and replaces childrenHtml with the rendered body, so container renderers must echo input.childrenHtml where the body belongs. Renderers are synchronous and must escape any user input themselves — escapeHtml and escapeHtmlAttribute are re-exported from @riebeckite/core.

Exports

  • shortcodes(options?) / shortcodesPlugin — plugin factory
  • remarkShortcodes(options?) — standalone remark transform
  • renderShortcode(request, options) — render a single shortcode
  • resolveShortcodeOptions(options?) — normalize options
  • builtinShortcodes, builtinShortcodeNames — the built-in registry
  • builtinInlineShortcodes — built-in names allowed in the inline : form
  • Types: ShortcodeRenderer, ShortcodeOptions, ResolvedShortcodeOptions, ShortcodeRenderInput, ShortcodeRenderRequest, ShortcodeAttributes, RemarkShortcodesOptions

See also

History

1 changesCollapseExpand
1 + <!-- Generated from packages/plugins/shortcodes/README.md. Do not edit this page directly; edit the package README and run `pnpm docs:sync`. -->
2 +
3 + # Shortcodes
4 +
5 + A generic shortcode system for Markdown, built on `remark-directive`.
6 +
7 + [日本語](./shortcodes.md)
8 +
9 + ## Overview
10 +
11 + `shortcodes()` turns `remark-directive` syntax into HTML at Markdown time. It
12 + supports inline shortcodes (`:name[label]{key=value}`), block leaf shortcodes
13 + (`::name[label]{key=value}`), and container shortcodes
14 + (`:::name[label]{attrs}` … `:::`), and ships an extensible registry so projects
15 + can add their own renderers on top of the built-ins.
16 +
17 + Inline shortcodes render as a `span` inside the surrounding paragraph; block and
18 + container shortcodes render as a `div`. Every wrapper carries
19 + `rb-shortcode rb-shortcode--<name>`.
20 +
21 + ## Usage
22 +
23 + ```ts
24 + import { defineConfig } from "@riebeckite/core";
25 + import { shortcodes } from "@riebeckite/plugin-shortcodes";
26 +
27 + export default defineConfig({
28 + // ...
29 + plugins: [shortcodes()],
30 + });
31 + ```
32 +
33 + Styles ship in `style.css`.
34 +
35 + ## Syntax
36 +
37 + ```
38 + :badge[New]{variant=success}
39 +
40 + ::kbd[Ctrl+S]
41 +
42 + ::youtube[id=dQw4w9WgXcQ]
43 +
44 + :::note[Heads up]{type=warning}
45 + Container bodies support **Markdown**.
46 + :::
47 + ```
48 +
49 + - `:name[label]{key=value}` — inline shortcode, rendered as a `span` inside the
50 + current paragraph. Only names in `inlineShortcodes` can be used inline;
51 + built-ins opt in with `badge`, `kbd`, `link-card`, and `file`.
52 + - `::name[label]{key=value}` — block leaf shortcode, rendered as a `div`.
53 + - `:::name[label]{key=value}` … `:::` — container shortcode; the body is
54 + rendered by the normal Markdown pipeline.
55 +
56 + A shortcode that is only defined as a block emits a
57 + `shortcodes-inline-unsupported` diagnostic when written with `:` and stays on
58 + the page as escaped text. Unknown shortcodes emit a `shortcodes-unknown`
59 + diagnostic, and malformed attributes emit `shortcodes-invalid` and are ignored.
60 +
61 + ## Built-in shortcodes
62 +
63 + | Name | Kind | Inline | Attributes | Output |
64 + | ---- | ---- | ------ | ---------- | ------ |
65 + | `figure` | leaf / container | – | `src`/`url`/`image`, `alt`, `caption`, `width`, `height` | `<figure>` with image and caption |
66 + | `youtube` | leaf | – | `id`/`video` or `url`/`src`, `title` | Privacy-friendly `youtube-nocookie.com` embed |
67 + | `vimeo` | leaf | – | `id`/`video` or `url`/`src`, `title` | `player.vimeo.com` embed (`dnt=1`) |
68 + | `gist` | leaf | – | `user` + `id`, or `url`, `file` | GitHub Gist embed with `<noscript>` link |
69 + | `kbd` | leaf | yes | label or `keys` | `<kbd>` elements split on `+` |
70 + | `badge` | leaf | yes | label or `text`, `variant`/`type`/`color`, `title` | Inline badge |
71 + | `details` | container | – | label or `summary`, `open` | `<details>` disclosure |
72 + | `spoiler` | container | – | label or `summary`, `open` | `details` alias with a different class |
73 + | `note` | leaf / container | – | label or `title`, `type`/`variant` | Callout box |
74 + | `callout` | leaf / container | – | label or `title`, `type`/`variant` | `note` alias with a different class |
75 + | `link-card` | leaf | yes | `url`/`href`, `title`, `description`, `image`/`icon` | Link preview card |
76 + | `file` | leaf | yes | `url`/`src`/`path`, `name`/`label`, `size` | Download link |
77 +
78 + ## Options
79 +
80 + | Option | Type | Default | Description |
81 + | ------ | ---- | ------- | ----------- |
82 + | `className` | `string` | `"rb-shortcode"` | Root CSS class of every wrapper |
83 + | `language` | `string` | – | BCP-47 tag applied to wrappers as `lang` |
84 + | `builtins` | `boolean` | `true` | Register the built-in renderers |
85 + | `shortcodes` | `Record<string, ShortcodeRenderer>` | `{}` | Custom renderers, merged over the built-ins |
86 + | `inlineShortcodes` | `readonly string[]` | `[]` | Extra names allowed in the inline `:` form |
87 +
88 + ## Custom renderers
89 +
90 + ```ts
91 + import { shortcodes } from "@riebeckite/plugin-shortcodes";
92 +
93 + shortcodes({
94 + shortcodes: {
95 + mark: ({ label, attributes }) => `<mark>${label}</mark>`,
96 + },
97 + inlineShortcodes: ["mark"],
98 + });
99 + ```
100 +
101 + A `ShortcodeRenderer` receives `{ name, label, attributes, childrenHtml,
102 + context, container }` and returns a string. A custom renderer can be used with
103 + the inline `:` form only when its name is listed in `inlineShortcodes`. For
104 + container shortcodes the plugin wraps the result in
105 + `<div class="rb-shortcode rb-shortcode--<name>">` and replaces `childrenHtml`
106 + with the rendered body, so container renderers must echo `input.childrenHtml`
107 + where the body belongs. Renderers are synchronous and must escape any user
108 + input themselves — `escapeHtml` and `escapeHtmlAttribute` are re-exported from
109 + `@riebeckite/core`.
110 +
111 + ## Exports
112 +
113 + - `shortcodes(options?)` / `shortcodesPlugin` — plugin factory
114 + - `remarkShortcodes(options?)` — standalone remark transform
115 + - `renderShortcode(request, options)` — render a single shortcode
116 + - `resolveShortcodeOptions(options?)` — normalize options
117 + - `builtinShortcodes`, `builtinShortcodeNames` — the built-in registry
118 + - `builtinInlineShortcodes` — built-in names allowed in the inline `:` form
119 + - Types: `ShortcodeRenderer`, `ShortcodeOptions`, `ResolvedShortcodeOptions`,
120 + `ShortcodeRenderInput`, `ShortcodeRenderRequest`, `ShortcodeAttributes`,
121 + `RemarkShortcodesOptions`
122 +
123 + ## See also
124 +
125 + - [Plugin guide](../reference/plugin-api.en.md)
126 +