npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

remark-dgmo

v0.14.2

Published

Remark plugin to render DGMO diagrams from fenced code blocks at build time. Framework-agnostic core shared by astro-dgmo, docusaurus-plugin-dgmo, and any unified pipeline.

Readme

remark-dgmo

Framework-agnostic remark plugin that renders DGMO diagrams from ```dgmo fenced code blocks at build time. Powered by @diagrammo/dgmo. Zero client JavaScript by default.

📖 Setup guides for Astro, Docusaurus & Fumadocs: diagrammo.app/embed

sequence
Client -POST /login-> API
API -validate-> Auth
Auth -JWT-> API
API -200 OK-> Client

Drop a fenced block with the language dgmo into any markdown or MDX file processed by a unified-style pipeline — Astro, Docusaurus, Starlight, Vitepress, eleventy-with-remark, or your own custom toolchain — and it becomes an inline <svg> at build time.

By default, every diagram is rendered twice (once with the palette's light mode, once with its dark mode) and wrapped in <div class="dgmo-light"> / <div class="dgmo-dark">. A tiny shipped stylesheet hides the wrong one based on [data-theme="dark"] (the convention used by Docusaurus, Starlight, and most other docs frameworks). The result: your diagrams follow the host page's color-mode toggle without any client-side rendering.

Install

pnpm add remark-dgmo @diagrammo/dgmo
# or
npm install remark-dgmo @diagrammo/dgmo

@diagrammo/dgmo is a peer dependency.

ESM-only. Your config file must be .mjs, .ts, or .mts — or your package.json must have "type": "module".

Use — three integration patterns

Pattern 1: Astro

Use astro-dgmo — it wraps this plugin and handles the integration plumbing.

pnpm add astro-dgmo @diagrammo/dgmo
// astro.config.mjs
import { defineConfig } from 'astro/config';
import dgmo from 'astro-dgmo';

export default defineConfig({
  integrations: [dgmo()],
});

You'll also need to import the color-mode stylesheet in your global layout:

---
// src/layouts/Base.astro
import 'remark-dgmo/client.css';
---

Pattern 2: Docusaurus

Use docusaurus-plugin-dgmo — it handles getClientModules() registration for the CSS + client script.

pnpm add docusaurus-plugin-dgmo @diagrammo/dgmo
// docusaurus.config.ts
import type { Config } from '@docusaurus/types';

const config: Config = {
  // …
  plugins: ['docusaurus-plugin-dgmo'],
  presets: [
    [
      'classic',
      {
        docs: {
          remarkPlugins: [
            (await import('docusaurus-plugin-dgmo/remark')).default,
          ],
        },
        blog: {
          remarkPlugins: [
            (await import('docusaurus-plugin-dgmo/remark')).default,
          ],
        },
        pages: {
          remarkPlugins: [
            (await import('docusaurus-plugin-dgmo/remark')).default,
          ],
        },
      },
    ],
  ],
};

export default config;

The plugin registers client.css + client.js via getClientModules(). You still wire remarkPlugins into each preset slot manually — Docusaurus's plugin API has no hook to auto-inject into a sibling preset.

Pattern 3: Fumadocs (Next.js app router)

Use fumadocs-dgmo — it wraps mdxOptions for fumadocs-mdx, ships a .dark-rewritten stylesheet (Fumadocs UI's next-themes default), and provides a Client Component that re-binds on every soft navigation.

pnpm add fumadocs-dgmo @diagrammo/dgmo
// source.config.ts
import { defineConfig } from 'fumadocs-mdx/config';
import { withDgmo } from 'fumadocs-dgmo/config';

export default defineConfig({
  mdxOptions: withDgmo(),
});
/* app/global.css */
@import 'fumadocs-ui/css/preset.css';
@import 'fumadocs-dgmo/client.css';
// app/layout.tsx — add <DgmoClient /> inside <RootProvider>
import { DgmoClient } from 'fumadocs-dgmo/client';
// …
<RootProvider>
  {children}
  <DgmoClient />
</RootProvider>;

Pattern 4: Vanilla unified pipeline

import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import remarkDgmo from 'remark-dgmo';

const out = await unified()
  .use(remarkParse)
  .use(remarkDgmo, { mode: 'showcase', palette: 'catppuccin' })
  .use(remarkRehype, { allowDangerousHtml: true })
  .use(rehypeStringify, { allowDangerousHtml: true })
  .process(source);

In your output HTML's <head>, add the shipped stylesheet (or inline its three rules):

<link
  rel="stylesheet"
  href="/path/to/node_modules/remark-dgmo/dist/client.css"
/>
<script
  type="module"
  src="/path/to/node_modules/remark-dgmo/dist/client.js"
></script>

The client script is optional — it tightens each diagram's viewBox to its content bounds and wires up showcase-mode copy buttons. Without it, diagrams still render but may have extra whitespace and copy buttons won't function.

Options

remarkDgmo({
  // Output mode for `dgmo` blocks. 'diagram' (default) = SVG only.
  // 'showcase' = syntax-highlighted source + diagram + copy + open-in-editor.
  mode: 'diagram',

  // Default palette name (any registered @diagrammo/dgmo palette).
  palette: 'nord',

  // Color-mode strategy. 'auto' (default) renders both light and dark and
  // toggles via CSS. 'light' or 'dark' single-renders with the matching theme.
  colorMode: 'auto',

  // Default theme when colorMode is 'light' or 'dark' (single-render). Ignored under 'auto'.
  theme: 'dark',

  // Showcase chrome — each toolbar button toggles independently. Enabled
  // automatically in showcase mode; set any to false to hide just that button.
  showSource: undefined, // boolean; default = (mode === 'showcase')
  showCopy: undefined, // boolean; default = (mode === 'showcase')
  showExpand: undefined, // boolean; default = (mode === 'showcase')
  showOpenInEditor: undefined, // boolean; default = (mode === 'showcase')

  // Where the "Open in editor" link points.
  editorBaseUrl: 'https://online.diagrammo.app',

  // Outer wrapper element + class hook.
  wrapper: 'figure',
  className: 'dgmo',

  // Append additional class names to every emitted wrapper. Used by
  // astro-dgmo v0.3.0 to keep the legacy `astro-dgmo*` class names for one
  // minor cycle of backward compat.
  legacyClassNames: [],

  // Emit MDX-compatible output. Default: false (raw `html` mdast node).
  // Set to true when the host pipeline routes files through @mdx-js/mdx —
  // Docusaurus with `markdown.format: 'mdx'`, Astro `.mdx`, Fumadocs, etc.
  // The plugin then emits an `mdxJsxFlowElement` instead of an `html` node,
  // so MDX accepts the output without "Cannot handle unknown node `raw`".
  mdx: false,
});

Per-block overrides

Append options to the fence info string. Tokens are space-separated; values may be quoted.

```dgmo showcase title="Login flow" palette=catppuccin theme=light
sequence
A -> B
```

| Token | Effect | | ------------------------------------------------------- | ----------------------------------- | | diagram / showcase | Set mode for this block | | palette=<name> | Override palette | | theme=light / theme=dark / theme=transparent | Override theme (single-render only) | | colorMode=auto / colorMode=light / colorMode=dark | Override color-mode strategy | | title="…" | Add a caption (<figcaption>) | | source / noSource | Force source view + toggle on/off | | copy / noCopy | Force copy button on/off | | expand / noExpand | Force expand (full-screen) on/off | | openInEditor / noOpenInEditor | Force editor link on/off |

Each toolbar button is independent — e.g. ```dgmo showcase noSource noExpand keeps just the copy + open-in-editor buttons, and ```dgmo copy adds only a copy button to an otherwise bare diagram.

Live links (on by default)

A fence can name a diagram living in Diagrammo Cloud instead of carrying its own source:

```dgmo
live-link dgm_01HQ3RSTUV
```

The build fetches that diagram's source, renders it exactly like a pasted one (fence-meta and per-block overrides all still apply), and writes what it fetched into .dgmo/references/<id>.json. Commit that directory. Three spellings are accepted — live-link <id> in a fence, ![[live-link:<id>]] in a note, or the plain share URL.

// on by default — this turns it OFF
remarkDgmo({ liveLink: { enabled: false } });

Switched off, a live-link fence renders a small card naming the diagram and linking through to it, plus a hover-revealed "Show this diagram here" link to the guide, and the build warns naming the file and line. Nothing is fetched. See the live links guide.

Only published diagrams can be referenced. A private diagram is not fetchable at all — there are no tokens, no signed links, and no origin allowlists to configure, which is also why every referenced byte is cacheable.

Why the cache is committed

So that our availability is never your build's problem. A clean CI checkout renders from the committed copy if we are unreachable, and a diagram changing shows up in your pull request as a source diff you can read.

| What happened | Your build | Your page | | -------------------------------- | --------------------------------- | ------------------------- | | all well | writes the cache | the current diagram | | we're unreachable, cache present | succeeds, with a warning | last known good | | we're unreachable, no cache | fails | — | | unknown id, never cached | fails (it can only be a typo) | — | | id gone, cache present | succeeds, with a warning | last known good | | the author unshared it | succeeds, with a warning | a "no longer shared" card |

Set liveLink: { offline: true } to skip the network entirely and build from the cache alone.

⚠️ Content-Security-Policy

If your site sets a CSP, it must allow connect-src https://api.diagrammo.app. Without it the diagram still renders — it was baked at build time — but it will never refresh, and nothing on the page can tell you so, because the report would be blocked too. This is the one thing to get right before shipping live links.

How the refresh works

remark-dgmo/client.js checks, once the page is idle, whether any referenced diagram has moved since the build. Almost always it hasn't, and the check costs one edge-cached request.

When one has, the default is to say so — a small link to the live diagram — rather than to re-render it. That default is about your bundle, not about laziness: re-rendering needs the dgmo renderer in the browser, and a bundler that can see the import ships it whether or not it is ever used. On this repo's own Astro fixture the difference is 1 chunk / 8.9 KB gzipped versus 88 chunks / 634 KB. Lazy for your readers; not free for your dist/.

If you want the swap anyway — a docs site that publishes far more often than it rebuilds is the case that wants it — opt in:

import 'remark-dgmo/client.js';
import 'remark-dgmo/client-render.js'; // adds the renderer to your bundle

That second line registers a renderer by running and exports nothing, so it needs a remark-dgmo newer than 0.14.1 to survive your build: up to and including that version the package declared itself free of side effects, which licensed bundlers to delete the import outright — silently, leaving you the link and no renderer. If you are wiring this from application code rather than a side-effect import, a dynamic import('remark-dgmo/client-render.js') works on any version and is what the framework wrappers do.

Using a wrapper? Each one has its own way in, and setting refresh: 'render' without it now tells you so at build time: astro-dgmo and docusaurus-plugin-dgmo inject the runtime for you, while fumadocs-dgmo and nextra-dgmo want <DgmoRenderClient /> mounted and vitepress-dgmo wants setupDgmoRender() called in your theme.

Even then, the client refuses a swap it cannot make safely: a renderer version that disagrees with the one that baked the page, or a new diagram that would reflow your layout. Those fall back to the same small link, and your diagram is left exactly as it was.

Working reference site

For an end-to-end example of remark-dgmo running inside a real framework, see docusaurus-plugin-dgmo's tests/fixture/ — a minimal Docusaurus 3 site that wires this plugin into every preset slot and exercises plain, tagged, showcase, and per-block-override blocks. The astro-dgmo repo has an equivalent Astro 6 fixture at tests/fixture/; the fumadocs-dgmo repo has a Next.js app-router fixture at tests/fixture/.

All three fixtures pin to link:../.. against the wrapper plugin's source, so they're the canonical reference for the smallest correct config — including the non-obvious gotchas (Docusaurus's async-function default export + markdown: { format: 'md' }, Astro's manual import 'remark-dgmo/client.css', Fumadocs's mdx: true requirement and html.dark selector mapping).

Custom color-mode selector

The shipped client.css keys on [data-theme="dark"] — the convention used by Docusaurus and Starlight. For Tailwind-style sites that signal dark mode via a .dark class on <html> (which is also what Fumadocs UI's next-themes default produces), don't import client.css directly — fumadocs-dgmo/client.css ships a build-time rewrite for that case. For any other custom selector, inline these three rules in your own CSS instead, swapping the selector:

.dgmo-dark {
  display: none;
}
html.dark .dgmo-light {
  display: none;
}
html.dark .dgmo-dark {
  display: block;
}

For data-color-scheme="dark", :root[data-mode="dark"], etc. — same three rules, swap the selector to match what your toggle sets.

How it works

  1. The remark transformer walks the mdast, finding code nodes with lang === 'dgmo'.
  2. For each block, renderDgmoBlock() calls render() from @diagrammo/dgmo — twice if colorMode: 'auto' (one light, one dark), once otherwise.
  3. Each SVG is normalized: width/height stripped, viewBox added, inline background removed.
  4. The original code node is replaced with an html node carrying the rendered wrapper(s).
  5. The optional client script (dist/client.js) tightens viewBoxes and binds showcase-mode copy buttons.

Rendering happens at build time. The browser sees only the inline SVG and the small color-mode CSS.

License

MIT