Remark Plugin

remark-codegloss is a unified remark plugin that transforms annotated fenced code blocks into CodeGloss instances at build time.

It supports two output modes:

  • mdx (default) — emits <CodeGloss /> MDX JSX. Use with MDX pipelines: @next/mdx, next-mdx-remote, Velite, Docusaurus.
  • html — emits a raw <code-gloss> HTML node with the config in a <script type="application/json"> child. Use with plain markdown pipelines (remark-rehyperehype-stringify) or anywhere outside of MDX.

How it works

The plugin walks the markdown AST looking for a specific pattern:

  1. A fenced code block with codegloss in its metadata
  2. Optionally followed by a json annotations code block

When it finds a match, it:

  • Replaces both blocks with a single CodeGloss node (JSX or HTML, depending on output mode)
  • Parses the annotations JSON and forwards its top-level keys (annotations, connections, arcs, callouts) as individual props — each key becomes its own prop on the emitted node, not merged
  • In mdx mode, auto-injects import { CodeGloss } from '@codegloss/react' (skippable via skipImport)

Detection pattern

The code block metadata must match this format:

  • lang (required) — the language identifier (js, ts, py, css, etc.)
  • codegloss (required) — the keyword that triggers detection
  • filename (optional) — displayed in the toolbar

Annotations block

The annotations block must immediately follow the codegloss block:

Four top-level keys are recognized:

  • annotations — array of Annotation objects. Each annotation can set "popover": true to switch its callout from inline-expanding to floating-popover presentation, and "defaultOpen": true to pre-open it on first render.
  • connections — array of Connection objects. Each connection can set "side": "right" to render on the right side of the block instead of in the left gutter, and "defaultOpen": true to pre-open its popover on first render.
  • arcs — arc-style overrides forwarded verbatim as a prop. Most commonly used for "arrowhead": true to turn on arrowheads at the to endpoint. See Arc style options for the full field list.
  • callouts — block-level callout behavior overrides. Set "popover": true to make every annotation in the block open as a floating popover. See Callout options.

See Pre-opening callouts for the full rules around defaultOpen — at most one annotation + one connection, last-wins cascade when multiple are marked.

If no annotations block follows, the component renders the code without annotations.

Options

type RemarkCodeglossOptions = {
  /**
   * Output mode. Default: 'mdx'.
   * - 'mdx': emits CodeGloss MDX JSX (use with MDX pipelines)
   * - 'html': emits a raw <code-gloss> HTML node (use with plain markdown pipelines)
   */
  output?: 'mdx' | 'html';
  /**
   * (mdx mode only) Skip injecting `import { CodeGloss } from '@codegloss/react'`.
   * Set true when CodeGloss is provided via a custom MDX components map.
   */
  skipImport?: boolean;
  /**
   * Default theme applied to all code blocks unless the block specifies its own.
   * Accepts a bundled theme name (e.g. 'github-dark', 'dracula').
   */
  theme?: string;
};

Applying a global theme

Pass a theme option to apply a default theme to all code blocks processed by the plugin:

remarkCodegloss({ theme: 'github-dark' })

Individual code blocks can override the global default — the remark plugin only injects the theme when the block doesn't specify its own.

Configuration examples

MDX mode (default)

Next.js with @next/mdx

import createMdx from '@next/mdx';
import remarkCodegloss from 'codegloss/remark';

const withMdx = createMdx({
  options: {
    remarkPlugins: [remarkCodegloss],
  },
});

Docusaurus

import remarkCodegloss from 'codegloss/remark';

export default {
  presets: [
    ['classic', {
      docs: {
        remarkPlugins: [[remarkCodegloss, { skipImport: true }]],
      },
    }],
  ],
};

skipImport: true is recommended in Docusaurus because the component is provided via the swizzled MDX components map (see the Docusaurus setup guide).

Non-React MDX (Vue, Preact)

The auto-injected import targets @codegloss/react, which matches the dominant @mdx-js/react consumers (Next.js, Docusaurus, Velite, Astro's MDX integration). If you're using @mdx-js/vue or @mdx-js/preact, set skipImport: true and provide CodeGloss via your MDX components mapping — point it at @codegloss/vue or your own wrapper:

// mdx config
remarkPlugins: [[remarkCodegloss, { skipImport: true }]],
// MDX components mapping
import { CodeGloss } from '@codegloss/vue';
export const components = { CodeGloss };

For SvelteKit (mdsvex) and VitePress, prefer output: 'html' instead — it emits a raw <code-gloss> element with no import at all.

Generic MDX pipeline

import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkMdx from 'remark-mdx';
import remarkCodegloss from 'codegloss/remark';

const processor = unified()
  .use(remarkParse)
  .use(remarkMdx)
  .use(remarkCodegloss);

HTML mode (plain markdown)

For pipelines that produce plain HTML (no JSX, no React):

import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import remarkCodegloss from 'codegloss/remark';

const html = await unified()
  .use(remarkParse)
  .use(remarkCodegloss, { output: 'html' })
  .use(remarkRehype, { allowDangerousHtml: true })
  .use(rehypeStringify, { allowDangerousHtml: true })
  .process(markdownSource);

The output is a raw <code-gloss> element with the config in a <script type="application/json"> child:

<code-gloss><script type="application/json">{"code":"...","lang":"js",...}</script></code-gloss>

You're responsible for loading the codegloss runtime in your HTML page so the custom element gets registered:

<script type="module" src="https://unpkg.com/codegloss/dist/index.js"></script>

This pattern works with marked, markdown-it, and any static site generator that goes through remark.

Edge cases

  • Invalid JSON — if the annotations block contains malformed JSON, the plugin logs a warning and renders the code block without annotations
  • Missing annotations block — the codegloss block renders as a CodeGloss instance with just the code, no annotations
  • Duplicate imports — in mdx mode, the plugin checks for existing CodeGloss imports before injecting one
  • Nested blocks — the plugin traverses child nodes recursively, so codegloss blocks inside blockquotes or other containers are detected
  • Script tag escaping — in html mode, the JSON payload escapes </script sequences to prevent breaking out of the script tag