Skip to content

Commit 762b569

Browse files
authored
feat: server-side processing (#451)
* basic server util poc * add todo * more comments * wip * update server implementation + tests * fix tests * add example * update snaps * misc fixes * fix lint * fix tests * fix site build * useCallback for ref * remove comment * update comments * comment feedback + doc changes * revert style change * add docs * move example * update docs * fix styles * fix docs
1 parent c73daa6 commit 762b569

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1680
-249
lines changed

docs/pages/docs/editor-api/_meta.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
2-
"manipulating-blocks":"",
3-
"manipulating-inline-content":"",
4-
"cursor-selections":"",
5-
"converting-blocks":""
2+
"manipulating-blocks": "",
3+
"manipulating-inline-content": "",
4+
"cursor-selections": "",
5+
"converting-blocks": "",
6+
"server-processing": ""
67
}

docs/pages/docs/editor-api/converting-blocks.mdx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,30 @@ Tries to create `Block` and `InlineContent` objects based on Markdown syntax, th
6363

6464
<Example name="interoperability/converting-blocks-from-md" />
6565

66-
## HTML
66+
## Export HTML (for static rendering)
6767

68-
The editor exposes functions to convert Blocks to and from HTML. Converting Blocks to HTML will lose some information such as the nesting of nodes in order to export a simple HTML structure.
68+
Use `blocksToFullHTML` to export the entire document with all structure, styles and formatting.
69+
The exported HTML is the same as BlockNote would use to render the editor, and includes all structure for nested blocks.
70+
71+
For example, you an use this for static rendering documents that have been created in the editor.
72+
Make sure to include the same stylesheets when you want to render the output HTML ([see example](/examples/backend/rendering-static-documents)).
73+
74+
```typescript
75+
blocksToFullHTML(blocks?: Block[]): string;
76+
77+
// Usage
78+
const HTMLFromBlocks = editor.blocksToFullHTML(blocks);
79+
```
80+
81+
`blocks:` The blocks to convert. If not provided, the entire document (all top-level blocks) is used.
82+
83+
`returns:` The blocks, exported to an HTML string.
84+
85+
## HTML (for interoperability)
86+
87+
The editor exposes functions to convert Blocks to and from HTML for interoperability with other applications.
88+
89+
Converting Blocks to HTML this way will lose some information such as the nesting of nodes in order to export a simple HTML structure.
6990

7091
### Converting Blocks to HTML
7192

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
title: Server-side processing
3+
description: Use `ServerBlockNoteEditor` to process Blocks on the server.
4+
imageTitle: Server-side processing
5+
path: /docs/server-side-processing
6+
---
7+
8+
import { Example } from "@/components/example";
9+
import { Callout } from "nextra/components";
10+
11+
# Server-side processing
12+
13+
While you can use the `BlockNoteEditor` on the client side,
14+
you can also use `ServerBlockNoteEditor` from `@blocknote/server-util` to process BlockNote documents on the server.
15+
16+
For example, use the following code to convert a BlockNote document to HTML on the server:
17+
18+
```tsx
19+
import { ServerBlockNoteEditor } from "@blocknote/server-util";
20+
21+
const editor = ServerBlockNoteEditor.create();
22+
const html = await editor.blocksToFullHTML(blocks);
23+
```
24+
25+
`ServerBlockNoteEditor.create` takes the same BlockNoteEditorOptions as `useCreateBlockNote` and `BlockNoteEditor.create` ([see docs](/docs/editor-basics/setup)),
26+
so you can pass the same configuration (for example, your custom schema) to your server-side BlockNote editor as on the client.
27+
28+
## Functions for converting blocks
29+
30+
`ServerBlockNoteEditor` exposes the same functions for [converting blocks as the client side editor](/docs/converting-blocks):
31+
32+
- `blocksToFullHTML`
33+
- `blocksToHTMLLossy` and `tryParseHTMLToBlocks`
34+
- `blocksToMarkdownLossy` and `tryParseMarkdownToBlocks`
35+
36+
## Yjs processing
37+
38+
Additionally, `ServerBlockNoteEditor` provides functions for processing Yjs documents in case you use Yjs collaboration:
39+
40+
- `yDocToBlocks` or `yXmlFragmentToBlocks`: use this to convert a Yjs document or XML Fragment to BlockNote blocks
41+
- `blocksToYDoc` or `blocksToYXmlFragment`: use this to convert a BlockNote document (blocks) to a Yjs document or XML Fragment
42+
43+
## React compatibility
44+
45+
If you use [custom schemas in React](/docs/custom-schemas), you can use the same schema on the server side.
46+
Functions like `blocksToFullHTML` will use your custom React rendering functions to export blocks to HTML, similar to how these functions work on the client.
47+
However, it could be that your React components require access to a React context (e.g. a theme or localization context).
48+
49+
For these use-cases, we provide a function `withReactContext` that allows you to pass a React context to the server-side editor.
50+
This example exports a BlockNote document to HTML within a React context `YourContext`, so that even Custom Blocks built in React that require `YourContext` will be exported correctly:
51+
52+
```tsx
53+
const html = await editor.withReactContext(
54+
({ children }) => (
55+
<YourContext.Provider value={true}>{children}</YourContext.Provider>
56+
),
57+
async () => editor.blocksToFullHTML(blocks),
58+
);
59+
```
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"playground": true,
3+
"docs": true,
4+
"author": "yousefed",
5+
"tags": ["server"],
6+
"dependencies": {
7+
"@blocknote/server-util": "latest"
8+
}
9+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import "@blocknote/core/fonts/inter.css";
2+
import "@blocknote/core/style.css";
3+
4+
/**
5+
On Server Side, you can use the ServerBlockNoteEditor to render BlockNote documents to HTML. e.g.:
6+
7+
import { ServerBlockNoteEditor } from "@blocknote/server-util";
8+
9+
const editor = ServerBlockNoteEditor.create();
10+
const html = await editor.blocksToFullHTML(document);
11+
12+
You can then use render this HTML as a static page or send it to the client. Make sure to include the editor stylesheets:
13+
14+
import "@blocknote/core/fonts/inter.css";
15+
import "@blocknote/core/style.css";
16+
17+
This example has the HTML hard-coded, but shows at least how the document will be rendered when the appropriate style sheets are loaded.
18+
*/
19+
20+
export default function App() {
21+
// This HTML is generated by the ServerBlockNoteEditor.blocksToFullHTML method
22+
const html = `<div class="bn-block-group" data-node-type="blockGroup">
23+
<div class="bn-block-outer" data-node-type="blockOuter" data-id="1" data-text-color="yellow" data-background-color="blue">
24+
<div class="bn-block" data-node-type="blockContainer" data-id="1" data-text-color="yellow" data-background-color="blue">
25+
<div class="bn-block-content" data-content-type="heading" data-text-alignment="right" data-level="2">
26+
<h2 class="bn-inline-content">
27+
<strong><u>Heading </u></strong><em><s>2</s></em>
28+
</h2>
29+
</div>
30+
<div class="bn-block-group" data-node-type="blockGroup">
31+
<div class="bn-block-outer" data-node-type="blockOuter" data-id="2" data-background-color="red">
32+
<div class="bn-block" data-node-type="blockContainer" data-id="2" data-background-color="red">
33+
<div class="bn-block-content" data-content-type="paragraph">
34+
<p class="bn-inline-content">Paragraph</p>
35+
</div>
36+
</div>
37+
</div>
38+
<div class="bn-block-outer" data-node-type="blockOuter" data-id="3">
39+
<div class="bn-block" data-node-type="blockContainer" data-id="3">
40+
<div class="bn-block-content" data-content-type="bulletListItem">
41+
<p class="bn-inline-content">list item</p>
42+
</div>
43+
</div>
44+
</div>
45+
</div>
46+
</div>
47+
</div>
48+
</div>
49+
`;
50+
51+
// Renders the editor instance using a React component.
52+
return (
53+
<div className="bn-container">
54+
<div
55+
className=" bn-default-styles"
56+
dangerouslySetInnerHTML={{ __html: html }}
57+
/>
58+
</div>
59+
);
60+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Rendering static documents
2+
3+
This example shows how you can use HTML exported using the `blocksToFullHTML` and render it as a static document (a view-only document, without the editor). You can use this for example if you use BlockNote to edit blog posts in a CMS, but want to display non-editable static, published pages to end-users.
4+
5+
**Relevant Docs:**
6+
7+
- [Server-side processing](/docs/editor-api/server-processing)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<html lang="en">
2+
<head>
3+
<script>
4+
<!-- AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY -->
5+
</script>
6+
<meta charset="UTF-8" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8+
<title>Rendering static documents</title>
9+
</head>
10+
<body>
11+
<div id="root"></div>
12+
<script type="module" src="./main.tsx"></script>
13+
</body>
14+
</html>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY
2+
import React from "react";
3+
import { createRoot } from "react-dom/client";
4+
import App from "./App";
5+
6+
const root = createRoot(document.getElementById("root")!);
7+
root.render(
8+
<React.StrictMode>
9+
<App />
10+
</React.StrictMode>
11+
);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "@blocknote/example-rendering-static-documents",
3+
"description": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY",
4+
"private": true,
5+
"version": "0.12.4",
6+
"scripts": {
7+
"start": "vite",
8+
"dev": "vite",
9+
"build": "tsc && vite build",
10+
"preview": "vite preview",
11+
"lint": "eslint . --max-warnings 0"
12+
},
13+
"dependencies": {
14+
"@blocknote/core": "latest",
15+
"@blocknote/react": "latest",
16+
"@blocknote/ariakit": "latest",
17+
"@blocknote/mantine": "latest",
18+
"@blocknote/shadcn": "latest",
19+
"react": "^18.3.1",
20+
"react-dom": "^18.3.1",
21+
"@blocknote/server-util": "latest"
22+
},
23+
"devDependencies": {
24+
"@types/react": "^18.0.25",
25+
"@types/react-dom": "^18.0.9",
26+
"@vitejs/plugin-react": "^4.0.4",
27+
"eslint": "^8.10.0",
28+
"vite": "^4.4.8"
29+
},
30+
"eslintConfig": {
31+
"extends": [
32+
"../../../.eslintrc.js"
33+
]
34+
},
35+
"eslintIgnore": [
36+
"dist"
37+
]
38+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"__comment": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY",
3+
"compilerOptions": {
4+
"target": "ESNext",
5+
"useDefineForClassFields": true,
6+
"lib": [
7+
"DOM",
8+
"DOM.Iterable",
9+
"ESNext"
10+
],
11+
"allowJs": false,
12+
"skipLibCheck": true,
13+
"esModuleInterop": false,
14+
"allowSyntheticDefaultImports": true,
15+
"strict": true,
16+
"forceConsistentCasingInFileNames": true,
17+
"module": "ESNext",
18+
"moduleResolution": "Node",
19+
"resolveJsonModule": true,
20+
"isolatedModules": true,
21+
"noEmit": true,
22+
"jsx": "react-jsx",
23+
"composite": true
24+
},
25+
"include": [
26+
"."
27+
],
28+
"__ADD_FOR_LOCAL_DEV_references": [
29+
{
30+
"path": "../../../packages/core/"
31+
},
32+
{
33+
"path": "../../../packages/react/"
34+
}
35+
]
36+
}

0 commit comments

Comments
 (0)