Skip to content
GitHub

Saving and Loading Content

Saving and loading content is a fundamental requirement for any editor. ProseKit provides flexible options for persisting your content in different formats, primarily JSON and HTML. This guide will show you how to implement content persistence in your ProseKit editor.

ProseKit supports multiple content formats:

FormatDescriptionBest For
JSONNative ProseMirror document structureLong-term storage, preserving all features
HTMLStandard HTML markupInteroperability with other systems, display

JSON is the recommended format for storing ProseKit documents because it preserves the exact structure and all attributes of your content.

To get your document as JSON, use the getDocJSON() method:

// Get the current document as a JSON object
const jsonconst json: NodeJSON = editorconst editor: Editor<BasicExtension>.getDocJSONEditor<BasicExtension>.getDocJSON: () => NodeJSON
Return a JSON object representing the editor's current document.
()

To load content from JSON when creating an editor:

// Create editor with the loaded content
const editorconst editor: Editor<BasicExtension> = createEditorcreateEditor<BasicExtension>(options: EditorOptions<BasicExtension>): Editor<BasicExtension>
@public
({
extensionEditorOptions<BasicExtension>.extension: BasicExtension
The extension to use when creating the editor.
,
defaultContentEditorOptions<E extends Extension>.defaultContent?: string | NodeJSON | HTMLElement | undefined
The starting document to use when creating the editor. It can be a ProseMirror node JSON object, a HTML string, or a HTML element instance.
: jsonconst json: NodeJSON, // Pass the JSON object directly
})

To save content when the document changes, use the useDocChange hook:

import { useDocChangefunction useDocChange(handler: (doc: Node) => void, options?: UseExtensionOptions): void
Calls the given handler whenever the editor document changes.
@public
} from 'prosekit/react'
useDocChangefunction useDocChange(handler: (doc: Node) => void, options?: UseExtensionOptions): void
Calls the given handler whenever the editor document changes.
@public
(() => {
// This runs whenever the document changes const jsonconst json: NodeJSON = editorconst editor: Editor<BasicExtension>.getDocJSONEditor<BasicExtension>.getDocJSON: () => NodeJSON
Return a JSON object representing the editor's current document.
()
localStoragevar localStorage: Storage
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/localStorage)
.setItemStorage.setItem(key: string, value: string): void
Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously. Throws a "QuotaExceededError" DOMException exception if the new value couldn't be set. (Setting could fail if, e.g., the user has disabled storage for the site, or if the quota has been exceeded.) Dispatches a storage event on Window objects holding an equivalent Storage object. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Storage/setItem)
('my-document', JSONvar JSON: JSON
An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
.stringifyJSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)
Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
@paramvalue A JavaScript value, usually an object or array, to be converted.@paramreplacer A function that transforms the results.@paramspace Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
(jsonconst json: NodeJSON))
}, { editorUseExtensionOptions.editor?: Editor<any> | undefined
The editor to add the extension to. If not provided, it will use the editor from the nearest `ProseKit` component.
})

Here’s a complete example of saving and loading content using JSON:

import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'

import type { Editor } from 'prosekit/core'
import {
  ProseKit,
  useDocChange,
} from 'prosekit/react'

export default function EditorComponent({
  editor,
  onDocChange,
}: {
  editor: Editor
  onDocChange: () => void
}) {
  useDocChange(onDocChange, { editor })

  return (
    <ProseKit editor={editor}>
      <div className='relative w-full flex-1 box-border overflow-y-scroll'>
        <div ref={editor.mount} className='ProseMirror box-border min-h-full px-[max(4rem,_calc(50%-20rem))] py-8 outline-none outline-0 [&_span[data-mention="user"]]:text-blue-500 [&_span[data-mention="tag"]]:text-violet-500'></div>
      </div>
    </ProseKit>
  )
}

HTML is useful when you need to display your content outside the editor or integrate with systems that understand HTML.

To convert your document to HTML:

// Get the current document as an HTML string
const htmlconst html: string = editorconst editor: Editor<BasicExtension>.getDocHTMLEditor<BasicExtension>.getDocHTML: (options?: getDocHTMLOptions) => string
Return a HTML string representing the editor's current document.
()
// Store the HTML localStoragevar localStorage: Storage
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/localStorage)
.setItemStorage.setItem(key: string, value: string): void
Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously. Throws a "QuotaExceededError" DOMException exception if the new value couldn't be set. (Setting could fail if, e.g., the user has disabled storage for the site, or if the quota has been exceeded.) Dispatches a storage event on Window objects holding an equivalent Storage object. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Storage/setItem)
('my-document-html', htmlconst html: string)

To load content from HTML:

// Retrieve stored HTML
const htmlconst html: string = localStoragevar localStorage: Storage
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/localStorage)
.getItemStorage.getItem(key: string): string | null
Returns the current value associated with the given key, or null if the given key does not exist. [MDN Reference](https://developer.mozilla.org/docs/Web/API/Storage/getItem)
('my-document-html') || ''
// Create editor with the loaded HTML const editorconst editor: Editor<BasicExtension> = createEditorcreateEditor<BasicExtension>(options: EditorOptions<BasicExtension>): Editor<BasicExtension>
@public
({
extensionEditorOptions<BasicExtension>.extension: BasicExtension
The extension to use when creating the editor.
,
defaultContentEditorOptions<E extends Extension>.defaultContent?: string | NodeJSON | HTMLElement | undefined
The starting document to use when creating the editor. It can be a ProseMirror node JSON object, a HTML string, or a HTML element instance.
: htmlconst html: string, // Pass the HTML string
})

Here’s an example of saving and loading content using HTML:

import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'

import type { Editor } from 'prosekit/core'
import {
  ProseKit,
  useDocChange,
} from 'prosekit/react'

export default function EditorComponent({
  editor,
  onDocChange,
}: {
  editor: Editor
  onDocChange: () => void
}) {
  useDocChange(onDocChange, { editor })

  return (
    <ProseKit editor={editor}>
      <div className='relative w-full flex-1 box-border overflow-y-scroll'>
        <div ref={editor.mount} className='ProseMirror box-border min-h-full px-[max(4rem,_calc(50%-20rem))] py-8 outline-none outline-0 [&_span[data-mention="user"]]:text-blue-500 [&_span[data-mention="tag"]]:text-violet-500'></div>
      </div>
    </ProseKit>
  )
}

While ProseKit doesn’t directly support Markdown as a storage format, you can add Markdown support using additional libraries.

Here’s an example of using Markdown for saving and loading. It uses the remark and rehype libraries to convert between Markdown and HTML.

import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'

import type { Editor } from 'prosekit/core'
import {
  ProseKit,
  useDocChange,
} from 'prosekit/react'

export default function EditorComponent({
  editor,
  onDocChange,
}: {
  editor: Editor
  onDocChange: () => void
}) {
  useDocChange(onDocChange, { editor })

  return (
    <ProseKit editor={editor}>
      <div className='relative w-full flex-1 box-border overflow-y-scroll'>
        <div ref={editor.mount} className='ProseMirror box-border min-h-full px-[max(4rem,_calc(50%-20rem))] py-8 outline-none outline-0 [&_span[data-mention="user"]]:text-blue-500 [&_span[data-mention="tag"]]:text-violet-500'></div>
      </div>
    </ProseKit>
  )
}

ProseKit provides utility functions for converting between plain JSON object, HTML string, and ProseMirrorNode.