Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/docusaurus-theme-mermaid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
"react-dom": "^18.0.0 || ^19.0.0",
"@mermaid-js/layout-elk": "^0.1.9"
},
"peerDependenciesMeta": {
"@mermaid-js/layout-elk": {
"optional": true
}
},
"engines": {
"node": ">=18.0"
Expand Down
8 changes: 6 additions & 2 deletions packages/docusaurus-theme-mermaid/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import {useState, useEffect, useMemo} from 'react';
import {useColorMode, useThemeConfig} from '@docusaurus/theme-common';
import mermaid from 'mermaid';
import {ensureLayoutsRegistered} from './layouts';

import type {RenderResult, MermaidConfig} from 'mermaid';
import type {ThemeConfig} from '@docusaurus/theme-mermaid';

Expand Down Expand Up @@ -37,7 +39,7 @@ function useMermaidId(): string {
Note: Mermaid doesn't like values provided by Rect.useId() and throws
*/

// TODO 2025-2026: check if useId() now works
// TODO Docusaurus v4: check if useId() now works
// It could work thanks to https://github.com/facebook/react/pull/32001
// return useId(); // tried that, doesn't work ('#d:re:' is not a valid selector.)

Expand All @@ -53,6 +55,8 @@ async function renderMermaid({
text: string;
config: MermaidConfig;
}): Promise<RenderResult> {
await ensureLayoutsRegistered();

/*
Mermaid API is really weird :s
It is a big mutable singleton with multiple config levels
Expand All @@ -71,7 +75,7 @@ async function renderMermaid({
To use a new mermaid config (on colorMode change for example) we should
update siteConfig, and it can only be done with initialize()
*/
mermaid.mermaidAPI.initialize(config);
mermaid.initialize(config);

try {
return await mermaid.render(id, text);
Expand Down
34 changes: 34 additions & 0 deletions packages/docusaurus-theme-mermaid/src/client/layouts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import mermaid from 'mermaid';

declare global {
// Global variable provided by bundler DefinePlugin
/* eslint-disable-next-line no-underscore-dangle */
const __DOCUSAURUS_MERMAID_LAYOUT_ELK_ENABLED__: boolean;
}

async function registerOptionalElkLayout() {
// Mermaid does not support ELK layouts by default
// See https://github.com/mermaid-js/mermaid/tree/develop/packages/mermaid-layout-elk
// ELK layouts are heavy, so we made it an optional peer dependency
// See https://github.com/facebook/docusaurus/pull/11357
if (__DOCUSAURUS_MERMAID_LAYOUT_ELK_ENABLED__) {
const elkLayout = (await import('@mermaid-js/layout-elk')).default;
mermaid.registerLayoutLoaders(elkLayout);
}
}

// Ensure we only try to register layouts once
let layoutsRegistered = false;
export async function ensureLayoutsRegistered(): Promise<void> {
if (!layoutsRegistered) {
await registerOptionalElkLayout();
layoutsRegistered = true;
}
}
36 changes: 35 additions & 1 deletion packages/docusaurus-theme-mermaid/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,26 @@

import type {Plugin} from '@docusaurus/types';

export default function themeMermaid(): Plugin<void> {
/**
* Check if the optional @mermaid-js/layout-elk package is available.
* It's an optional peer dependency because it's heavy and most Mermaid users
* might not need it.
*/
async function isElkLayoutPackageAvailable() {
try {
await import('@mermaid-js/layout-elk');
return true;
} catch (e) {
return false;
}
}

export default async function themeMermaid(): Promise<Plugin<void>> {
// For now, we infer based on package availability
// In the future, we could make it configurable so that users can disable it
// even if the package is installed?
const elkLayoutEnabled = await isElkLayoutPackageAvailable();

return {
name: 'docusaurus-theme-mermaid',

Expand All @@ -17,6 +36,21 @@ export default function themeMermaid(): Plugin<void> {
getTypeScriptThemePath() {
return '../src/theme';
},

configureWebpack(config, isServer, utils) {
return {
plugins: [
new utils.currentBundler.instance.DefinePlugin({
__DOCUSAURUS_MERMAID_LAYOUT_ELK_ENABLED__: JSON.stringify(
// We only need to include the layout registration code on the
// client side. This also solves a weird Webpack-only bug when
// compiling the server config due to the module being ESM-only.
!isServer && elkLayoutEnabled,
),
}),
],
};
},
};
}

Expand Down
1 change: 1 addition & 0 deletions project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Csapo
Csvg
Dabit
dabit
dagre
Daishi
Datagit
datagit
Expand Down
85 changes: 85 additions & 0 deletions website/_dogfooding/_pages tests/diagrams.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -447,3 +447,88 @@ architecture-beta
disk1:T -- B:server
disk2:T -- B:db
```

## ELK Styling

Mermaid provides an [ELK layout](https://mermaid.js.org/syntax/entityRelationshipDiagram.html#layout)

### Dagre

This is a "classical" Mermaid diagram, using the default Dagre layout.

```mermaid
erDiagram

COMPANY ||--o{ DEPARTMENT : has
COMPANY ||--o{ PROJECT : undertakes
COMPANY ||--o{ LOCATION : operates_in
COMPANY ||--o{ CLIENT : serves

DEPARTMENT ||--o{ EMPLOYEE : employs
DEPARTMENT ||--o{ PROJECT : manages
DEPARTMENT ||--o{ BUDGET : allocated

EMPLOYEE }o--o{ PROJECT : works_on
EMPLOYEE ||--|| ADDRESS : lives_at
EMPLOYEE }o--o{ SKILL : has
EMPLOYEE ||--o{ DEPENDENT : supports

PROJECT ||--o{ CLIENT : for
PROJECT ||--o{ TASK : contains

```

### ELK er diagram layout

This ER diagram should look different, using the ELK layout.

```mermaid
---
config:
layout: elk
---
erDiagram

COMPANY ||--o{ DEPARTMENT : has
COMPANY ||--o{ PROJECT : undertakes
COMPANY ||--o{ LOCATION : operates_in
COMPANY ||--o{ CLIENT : serves

DEPARTMENT ||--o{ EMPLOYEE : employs
DEPARTMENT ||--o{ PROJECT : manages
DEPARTMENT ||--o{ BUDGET : allocated

EMPLOYEE }o--o{ PROJECT : works_on
EMPLOYEE ||--|| ADDRESS : lives_at
EMPLOYEE }o--o{ SKILL : has
EMPLOYEE ||--o{ DEPENDENT : supports

PROJECT ||--o{ CLIENT : for
PROJECT ||--o{ TASK : contains

```

Mermaid also provides a way of setting config parameters using a directive `%%{init:{"layout":"elk"}}%%`

```mermaid
%%{init:{"layout":"elk"}}%%
erDiagram

COMPANY ||--o{ DEPARTMENT : has
COMPANY ||--o{ PROJECT : undertakes
COMPANY ||--o{ LOCATION : operates_in
COMPANY ||--o{ CLIENT : serves

DEPARTMENT ||--o{ EMPLOYEE : employs
DEPARTMENT ||--o{ PROJECT : manages
DEPARTMENT ||--o{ BUDGET : allocated

EMPLOYEE }o--o{ PROJECT : works_on
EMPLOYEE ||--|| ADDRESS : lives_at
EMPLOYEE }o--o{ SKILL : has
EMPLOYEE ||--o{ DEPENDENT : supports

PROJECT ||--o{ CLIENT : for
PROJECT ||--o{ TASK : contains

```
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,36 @@ import Mermaid from '@theme/Mermaid';
C-->D;`}
/>
```

## Layouts

Mermaid supports different [layout engines](https://mermaid.js.org/intro/syntax-reference.html#layout-and-look):

- The `dagre` layout engine is supported by default in Docusaurus.
- The `elk` layout engine is heavier and can be enabled by installing the optional `@mermaid-js/layout-elk` dependency.

````md
```mermaid
---
config:
layout: elk
---
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```
````

```mermaid
---
config:
layout: elk
---
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```
1 change: 1 addition & 0 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@docusaurus/theme-mermaid": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/utils-common": "3.8.1",
"@mermaid-js/layout-elk": "^0.1.9",
"clsx": "^2.0.0",
"color": "^4.2.3",
"fs-extra": "^11.1.1",
Expand Down
13 changes: 13 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2591,6 +2591,14 @@
dependencies:
"@types/mdx" "^2.0.0"

"@mermaid-js/layout-elk@^0.1.9":
version "0.1.9"
resolved "https://registry.yarnpkg.com/@mermaid-js/layout-elk/-/layout-elk-0.1.9.tgz#c773b9454875858a2f45412fe04502bccec83cf2"
integrity sha512-HuvaqFZBr6yT9PpWYockvKAZPJVd89yn/UjOYPxhzbZxlybL2v+2BjVCg7MVH6vRs1irUohb/s42HEdec1CCZw==
dependencies:
d3 "^7.9.0"
elkjs "^0.9.3"

"@mermaid-js/parser@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@mermaid-js/parser/-/parser-0.4.0.tgz#c1de1f5669f8fcbd0d0c9d124927d36ddc00d8a6"
Expand Down Expand Up @@ -7937,6 +7945,11 @@ electron-to-chromium@^1.5.160:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz#477b0957e42f071905a86f7c905a9848f95d2bdb"
integrity sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==

elkjs@^0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.9.3.tgz#16711f8ceb09f1b12b99e971b138a8384a529161"
integrity sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==

emittery@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
Expand Down
Loading