Skip to content
This repository was archived by the owner on Nov 2, 2025. It is now read-only.

Commit 3059107

Browse files
Feez2403slorber
andauthored
feat(mermaid): support elk layout (facebook#11357)
Co-authored-by: sebastien <lorber.sebastien@gmail.com>
1 parent c131034 commit 3059107

File tree

9 files changed

+215
-4
lines changed

9 files changed

+215
-4
lines changed

packages/docusaurus-theme-mermaid/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@
4646
},
4747
"peerDependencies": {
4848
"react": "^18.0.0 || ^19.0.0",
49-
"react-dom": "^18.0.0 || ^19.0.0"
49+
"react-dom": "^18.0.0 || ^19.0.0",
50+
"@mermaid-js/layout-elk": "^0.1.9"
51+
},
52+
"peerDependenciesMeta": {
53+
"@mermaid-js/layout-elk": {
54+
"optional": true
55+
}
5056
},
5157
"engines": {
5258
"node": ">=18.0"

packages/docusaurus-theme-mermaid/src/client/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import {useState, useEffect, useMemo} from 'react';
99
import {useColorMode, useThemeConfig} from '@docusaurus/theme-common';
1010
import mermaid from 'mermaid';
11+
import {ensureLayoutsRegistered} from './layouts';
12+
1113
import type {RenderResult, MermaidConfig} from 'mermaid';
1214
import type {ThemeConfig} from '@docusaurus/theme-mermaid';
1315

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

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

@@ -53,6 +55,8 @@ async function renderMermaid({
5355
text: string;
5456
config: MermaidConfig;
5557
}): Promise<RenderResult> {
58+
await ensureLayoutsRegistered();
59+
5660
/*
5761
Mermaid API is really weird :s
5862
It is a big mutable singleton with multiple config levels
@@ -71,7 +75,7 @@ async function renderMermaid({
7175
To use a new mermaid config (on colorMode change for example) we should
7276
update siteConfig, and it can only be done with initialize()
7377
*/
74-
mermaid.mermaidAPI.initialize(config);
78+
mermaid.initialize(config);
7579

7680
try {
7781
return await mermaid.render(id, text);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import mermaid from 'mermaid';
9+
10+
declare global {
11+
// Global variable provided by bundler DefinePlugin
12+
/* eslint-disable-next-line no-underscore-dangle */
13+
const __DOCUSAURUS_MERMAID_LAYOUT_ELK_ENABLED__: boolean;
14+
}
15+
16+
async function registerOptionalElkLayout() {
17+
// Mermaid does not support ELK layouts by default
18+
// See https://github.com/mermaid-js/mermaid/tree/develop/packages/mermaid-layout-elk
19+
// ELK layouts are heavy, so we made it an optional peer dependency
20+
// See https://github.com/facebook/docusaurus/pull/11357
21+
if (__DOCUSAURUS_MERMAID_LAYOUT_ELK_ENABLED__) {
22+
const elkLayout = (await import('@mermaid-js/layout-elk')).default;
23+
mermaid.registerLayoutLoaders(elkLayout);
24+
}
25+
}
26+
27+
// Ensure we only try to register layouts once
28+
let layoutsRegistered = false;
29+
export async function ensureLayoutsRegistered(): Promise<void> {
30+
if (!layoutsRegistered) {
31+
await registerOptionalElkLayout();
32+
layoutsRegistered = true;
33+
}
34+
}

packages/docusaurus-theme-mermaid/src/index.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,26 @@
77

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

10-
export default function themeMermaid(): Plugin<void> {
10+
/**
11+
* Check if the optional @mermaid-js/layout-elk package is available.
12+
* It's an optional peer dependency because it's heavy and most Mermaid users
13+
* might not need it.
14+
*/
15+
async function isElkLayoutPackageAvailable() {
16+
try {
17+
await import('@mermaid-js/layout-elk');
18+
return true;
19+
} catch (e) {
20+
return false;
21+
}
22+
}
23+
24+
export default async function themeMermaid(): Promise<Plugin<void>> {
25+
// For now, we infer based on package availability
26+
// In the future, we could make it configurable so that users can disable it
27+
// even if the package is installed?
28+
const elkLayoutEnabled = await isElkLayoutPackageAvailable();
29+
1130
return {
1231
name: 'docusaurus-theme-mermaid',
1332

@@ -17,6 +36,21 @@ export default function themeMermaid(): Plugin<void> {
1736
getTypeScriptThemePath() {
1837
return '../src/theme';
1938
},
39+
40+
configureWebpack(config, isServer, utils) {
41+
return {
42+
plugins: [
43+
new utils.currentBundler.instance.DefinePlugin({
44+
__DOCUSAURUS_MERMAID_LAYOUT_ELK_ENABLED__: JSON.stringify(
45+
// We only need to include the layout registration code on the
46+
// client side. This also solves a weird Webpack-only bug when
47+
// compiling the server config due to the module being ESM-only.
48+
!isServer && elkLayoutEnabled,
49+
),
50+
}),
51+
],
52+
};
53+
},
2054
};
2155
}
2256

project-words.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Csapo
4848
Csvg
4949
Dabit
5050
dabit
51+
dagre
5152
Daishi
5253
Datagit
5354
datagit

website/_dogfooding/_pages tests/diagrams.mdx

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,3 +447,88 @@ architecture-beta
447447
disk1:T -- B:server
448448
disk2:T -- B:db
449449
```
450+
451+
## ELK Styling
452+
453+
Mermaid provides an [ELK layout](https://mermaid.js.org/syntax/entityRelationshipDiagram.html#layout)
454+
455+
### Dagre
456+
457+
This is a "classical" Mermaid diagram, using the default Dagre layout.
458+
459+
```mermaid
460+
erDiagram
461+
462+
COMPANY ||--o{ DEPARTMENT : has
463+
COMPANY ||--o{ PROJECT : undertakes
464+
COMPANY ||--o{ LOCATION : operates_in
465+
COMPANY ||--o{ CLIENT : serves
466+
467+
DEPARTMENT ||--o{ EMPLOYEE : employs
468+
DEPARTMENT ||--o{ PROJECT : manages
469+
DEPARTMENT ||--o{ BUDGET : allocated
470+
471+
EMPLOYEE }o--o{ PROJECT : works_on
472+
EMPLOYEE ||--|| ADDRESS : lives_at
473+
EMPLOYEE }o--o{ SKILL : has
474+
EMPLOYEE ||--o{ DEPENDENT : supports
475+
476+
PROJECT ||--o{ CLIENT : for
477+
PROJECT ||--o{ TASK : contains
478+
479+
```
480+
481+
### ELK er diagram layout
482+
483+
This ER diagram should look different, using the ELK layout.
484+
485+
```mermaid
486+
---
487+
config:
488+
layout: elk
489+
---
490+
erDiagram
491+
492+
COMPANY ||--o{ DEPARTMENT : has
493+
COMPANY ||--o{ PROJECT : undertakes
494+
COMPANY ||--o{ LOCATION : operates_in
495+
COMPANY ||--o{ CLIENT : serves
496+
497+
DEPARTMENT ||--o{ EMPLOYEE : employs
498+
DEPARTMENT ||--o{ PROJECT : manages
499+
DEPARTMENT ||--o{ BUDGET : allocated
500+
501+
EMPLOYEE }o--o{ PROJECT : works_on
502+
EMPLOYEE ||--|| ADDRESS : lives_at
503+
EMPLOYEE }o--o{ SKILL : has
504+
EMPLOYEE ||--o{ DEPENDENT : supports
505+
506+
PROJECT ||--o{ CLIENT : for
507+
PROJECT ||--o{ TASK : contains
508+
509+
```
510+
511+
Mermaid also provides a way of setting config parameters using a directive `%%{init:{"layout":"elk"}}%%`
512+
513+
```mermaid
514+
%%{init:{"layout":"elk"}}%%
515+
erDiagram
516+
517+
COMPANY ||--o{ DEPARTMENT : has
518+
COMPANY ||--o{ PROJECT : undertakes
519+
COMPANY ||--o{ LOCATION : operates_in
520+
COMPANY ||--o{ CLIENT : serves
521+
522+
DEPARTMENT ||--o{ EMPLOYEE : employs
523+
DEPARTMENT ||--o{ PROJECT : manages
524+
DEPARTMENT ||--o{ BUDGET : allocated
525+
526+
EMPLOYEE }o--o{ PROJECT : works_on
527+
EMPLOYEE ||--|| ADDRESS : lives_at
528+
EMPLOYEE }o--o{ SKILL : has
529+
EMPLOYEE ||--o{ DEPENDENT : supports
530+
531+
PROJECT ||--o{ CLIENT : for
532+
PROJECT ||--o{ TASK : contains
533+
534+
```

website/docs/guides/markdown-features/markdown-features-diagrams.mdx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,36 @@ import Mermaid from '@theme/Mermaid';
9999
C-->D;`}
100100
/>
101101
```
102+
103+
## Layouts
104+
105+
Mermaid supports different [layout engines](https://mermaid.js.org/intro/syntax-reference.html#layout-and-look):
106+
107+
- The `dagre` layout engine is supported by default in Docusaurus.
108+
- The `elk` layout engine is heavier and can be enabled by installing the optional `@mermaid-js/layout-elk` dependency.
109+
110+
````md
111+
```mermaid
112+
---
113+
config:
114+
layout: elk
115+
---
116+
graph TD;
117+
A-->B;
118+
A-->C;
119+
B-->D;
120+
C-->D;
121+
```
122+
````
123+
124+
```mermaid
125+
---
126+
config:
127+
layout: elk
128+
---
129+
graph TD;
130+
A-->B;
131+
A-->C;
132+
B-->D;
133+
C-->D;
134+
```

website/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"@docusaurus/theme-mermaid": "3.8.1",
5454
"@docusaurus/utils": "3.8.1",
5555
"@docusaurus/utils-common": "3.8.1",
56+
"@mermaid-js/layout-elk": "^0.1.9",
5657
"clsx": "^2.0.0",
5758
"color": "^4.2.3",
5859
"fs-extra": "^11.1.1",

yarn.lock

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2591,6 +2591,14 @@
25912591
dependencies:
25922592
"@types/mdx" "^2.0.0"
25932593

2594+
"@mermaid-js/layout-elk@^0.1.9":
2595+
version "0.1.9"
2596+
resolved "https://registry.yarnpkg.com/@mermaid-js/layout-elk/-/layout-elk-0.1.9.tgz#c773b9454875858a2f45412fe04502bccec83cf2"
2597+
integrity sha512-HuvaqFZBr6yT9PpWYockvKAZPJVd89yn/UjOYPxhzbZxlybL2v+2BjVCg7MVH6vRs1irUohb/s42HEdec1CCZw==
2598+
dependencies:
2599+
d3 "^7.9.0"
2600+
elkjs "^0.9.3"
2601+
25942602
"@mermaid-js/parser@^0.4.0":
25952603
version "0.4.0"
25962604
resolved "https://registry.yarnpkg.com/@mermaid-js/parser/-/parser-0.4.0.tgz#c1de1f5669f8fcbd0d0c9d124927d36ddc00d8a6"
@@ -7937,6 +7945,11 @@ electron-to-chromium@^1.5.160:
79377945
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz#477b0957e42f071905a86f7c905a9848f95d2bdb"
79387946
integrity sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==
79397947

7948+
elkjs@^0.9.3:
7949+
version "0.9.3"
7950+
resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.9.3.tgz#16711f8ceb09f1b12b99e971b138a8384a529161"
7951+
integrity sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==
7952+
79407953
emittery@^0.13.1:
79417954
version "0.13.1"
79427955
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"

0 commit comments

Comments
 (0)