Running on Node.js
ProseKit can run in Node.js environments for background processing, CLI applications, and Server-Side Rendering (SSR). The primary limitation is that DOM-dependent APIs are unavailable unless you provide a DOM implementation.
Create an editor in Node.js
Section titled “Create an editor in Node.js”You can create an editor and execute commands just like in a browser environment:
import { defineBasicExtension function defineBasicExtension(): BasicExtension
Define a basic extension that includes some common functionality. You can
copy this function and customize it to your needs.
It's a combination of the following extension functions:
-
{@link
defineDoc
}
-
{@link
defineText
}
-
{@link
defineParagraph
}
-
{@link
defineHeading
}
-
{@link
defineList
}
-
{@link
defineBlockquote
}
-
{@link
defineImage
}
-
{@link
defineHorizontalRule
}
-
{@link
defineHardBreak
}
-
{@link
defineTable
}
-
{@link
defineCodeBlock
}
-
{@link
defineItalic
}
-
{@link
defineBold
}
-
{@link
defineUnderline
}
-
{@link
defineStrike
}
-
{@link
defineCode
}
-
{@link
defineLink
}
-
{@link
defineBaseKeymap
}
-
{@link
defineBaseCommands
}
-
{@link
defineHistory
}
-
{@link
defineDropCursor
}
-
{@link
defineGapCursor
}
-
{@link
defineVirtualSelection
}
-
{@link
defineModClickPrevention
} } from 'prosekit/basic'
import { createEditor function createEditor<E extends Extension>(options: EditorOptions<E>): Editor<E>
} from 'prosekit/core'
const extension const extension: BasicExtension
= defineBasicExtension function defineBasicExtension(): BasicExtension
Define a basic extension that includes some common functionality. You can
copy this function and customize it to your needs.
It's a combination of the following extension functions:
-
{@link
defineDoc
}
-
{@link
defineText
}
-
{@link
defineParagraph
}
-
{@link
defineHeading
}
-
{@link
defineList
}
-
{@link
defineBlockquote
}
-
{@link
defineImage
}
-
{@link
defineHorizontalRule
}
-
{@link
defineHardBreak
}
-
{@link
defineTable
}
-
{@link
defineCodeBlock
}
-
{@link
defineItalic
}
-
{@link
defineBold
}
-
{@link
defineUnderline
}
-
{@link
defineStrike
}
-
{@link
defineCode
}
-
{@link
defineLink
}
-
{@link
defineBaseKeymap
}
-
{@link
defineBaseCommands
}
-
{@link
defineHistory
}
-
{@link
defineDropCursor
}
-
{@link
defineGapCursor
}
-
{@link
defineVirtualSelection
}
-
{@link
defineModClickPrevention
} ()
const editor const editor: Editor<BasicExtension>
= createEditor createEditor<BasicExtension>(options: EditorOptions<BasicExtension>): Editor<BasicExtension>
({ extension EditorOptions<BasicExtension>.extension: BasicExtension
The extension to use when creating the editor. })
// Set content with JSON
editor const editor: Editor<BasicExtension>
.setContent Editor<BasicExtension>.setContent: (content: Node | NodeJSON | string | HTMLElement, selection?: SelectionJSON | Selection | "start" | "end") => void
Update the editor's document and selection. ({
type type: string
: 'doc',
content content: {
type: string;
content: {
type: string;
text: string;
}[];
}[]
: [
{ type NodeJSON.type: string
: 'paragraph', content NodeJSON.content?: NodeJSON[] | undefined
: [{ type NodeJSON.type: string
: 'text', text NodeJSON.text?: string | undefined
: 'Hello, Node.js!' }] },
],
})
// Run commands
editor const editor: Editor<BasicExtension>
.commands Editor<BasicExtension>.commands: ToCommandAction<{
setParagraph: [];
setHeading: [attrs?: HeadingAttrs | undefined];
insertHeading: [attrs?: HeadingAttrs | undefined];
... 59 more ...;
redo: [];
}>
All
{@link
CommandAction
}
s defined by the editor. .insertImage insertImage: CommandAction
(attrs?: ImageAttrs | undefined) => boolean
Execute the current command. Return `true` if the command was successfully
executed, otherwise `false`. ({
src ImageAttrs.src?: string | null | undefined
: 'https://example.com/logo.png',
width ImageAttrs.width?: number | null | undefined
: 120,
height ImageAttrs.height?: number | null | undefined
: 60,
})
// Get the document as JSON
const json const json: NodeJSON
= editor const editor: Editor<BasicExtension>
.getDocJSON Editor<BasicExtension>.getDocJSON: () => NodeJSON
Return a JSON object representing the editor's current document. ()
console var console: Console
.log Console.log(...data: any[]): void
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static) (json const json: NodeJSON
)
DOM-dependent APIs are unavailable
Section titled “DOM-dependent APIs are unavailable”In a standard Node.js environment there is no DOM, so methods requiring DOM access will throw errors:
// ❌ Mounting fails without DOM
editor const editor: Editor<BasicExtension>
.mount Editor<BasicExtension>.mount: (place: HTMLElement | null | undefined) => void
Mount the editor to the given HTML element.
Pass `null` or `undefined` to unmount the editor. (document var document: Document
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document) .createElement Document.createElement<"div">(tagName: "div", options?: ElementCreationOptions): HTMLDivElement (+2 overloads)
Creates an instance of the element for the specified tag. ('div'))
// ❌ HTML serialization requires DOM APIs
editor const editor: Editor<BasicExtension>
.getDocHTML Editor<BasicExtension>.getDocHTML: (options?: getDocHTMLOptions) => string
Return a HTML string representing the editor's current document. ()
// ❌ HTML parsing requires DOM APIs
editor const editor: Editor<BasicExtension>
.setContent Editor<BasicExtension>.setContent: (content: Node | NodeJSON | string | HTMLElement, selection?: SelectionJSON | Selection | "start" | "end") => void
Update the editor's document and selection. ('<p>HTML input</p>')
Two strategies can address the absence of DOM APIs in server environments.
SSR strategy 1: Browser pre-rendering
Section titled “SSR strategy 1: Browser pre-rendering”The simplest approach for server-side rendering is client-side pre-rendering: allow the browser to convert documents to HTML and store the result.
// Browser code
const html const html: string
= editor const editor: Editor<BasicExtension>
.getDocHTML Editor<BasicExtension>.getDocHTML: (options?: getDocHTMLOptions) => string
Return a HTML string representing the editor's current document. ()
await fetch function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/fetch) ('/api/save', { method RequestInit.method?: string | undefined
A string to set request's method. : 'POST', body RequestInit.body?: BodyInit | null | undefined
A BodyInit object or null to set request's body. : html const html: string
})
The backend can then serve this static HTML without requiring DOM access.
SSR strategy 2: Server-side DOM simulation
Section titled “SSR strategy 2: Server-side DOM simulation”For JSON-to-HTML conversion within Node.js, use a headless DOM library such as jsdom or happy-dom.
Using jsdom
Section titled “Using jsdom”import { JSDOM } from 'jsdom'
// Initialize virtual DOM
const dom = new JSDOM('')
const document = dom.window.document
editor.setContent({
type: 'doc',
content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Foo' }] }],
})
editor.commands.insertText({ text: 'Bar' })
// Provide the document to getDocHTML
const html = editor.getDocHTML({ document })
console.log(html) // => <div><p>BarFoo</p></div>
Using happy-dom
Section titled “Using happy-dom”import { Window } from 'happy-dom'
// Initialize virtual DOM
const window = new Window()
const document = window.document
editor.setContent({
type: 'doc',
content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Foo' }] }],
})
editor.commands.insertText({ text: 'Bar' })
// Provide the document to getDocHTML
const html = editor.getDocHTML({ document })
console.log(html) // => <div><p>BarFoo</p></div>