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.
Supported Formats
ProseKit supports multiple content formats:
Format | Description | Best For |
---|---|---|
JSON | Native ProseMirror document structure | Long-term storage, preserving all features |
HTML | Standard HTML markup | Interoperability with other systems, display |
Working with JSON
JSON is the recommended format for storing ProseKit documents because it preserves the exact structure and all attributes of your content.
Saving Content as JSON
To get your document as JSON, use the getDocJSON()
method:
// Get the current document as a JSON object
const json = editor.getDocJSON()
Loading Content from JSON
To load content from JSON when creating an editor:
// Create editor with the loaded content
const editor = createEditor({
extension,
defaultContent: json, // Pass the JSON object directly
})
Detecting Document Changes
To save content when the document changes, use the useDocChange
hook:
import { useDocChange } from 'prosekit/react'
useDocChange(() => {
// This runs whenever the document changes
const json = editor.getDocJSON()
localStorage.setItem('my-document', JSON.stringify(json))
}, { editor })
import { useDocChange } from 'prosekit/vue'
useDocChange(() => {
// This runs whenever the document changes
const json = editor.getDocJSON()
localStorage.setItem('my-document', JSON.stringify(json))
}, { editor })
import { useDocChange } from 'prosekit/svelte'
useDocChange(() => {
// This runs whenever the document changes
const json = editor.getDocJSON()
localStorage.setItem('my-document', JSON.stringify(json))
}, { editor })
import { useDocChange } from 'prosekit/preact'
useDocChange(() => {
// This runs whenever the document changes
const json = editor.getDocJSON()
localStorage.setItem('my-document', JSON.stringify(json))
}, { editor })
import { useDocChange } from 'prosekit/solid'
useDocChange(() => {
// This runs whenever the document changes
const json = editor.getDocJSON()
localStorage.setItem('my-document', JSON.stringify(json))
}, { editor })
JSON Example
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>
)
}
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import { defineBasicExtension } from 'prosekit/basic'
import {
createEditor,
type NodeJSON,
} from 'prosekit/core'
import {
useCallback,
useMemo,
useState,
} from 'react'
import EditorComponent from './editor-component'
export default function Editor() {
const [defaultContent, setDefaultContent] = useState<NodeJSON | undefined>()
const [records, setRecords] = useState<string[]>([])
const [hasUnsavedChange, setHasUnsavedChange] = useState(false)
const [key, setKey] = useState(1)
// Create a new editor instance whenever `defaultContent` changes
const editor = useMemo(() => {
const extension = defineBasicExtension()
return createEditor({ extension, defaultContent })
}, [defaultContent])
// Enable the save button
const handleDocChange = useCallback(() => setHasUnsavedChange(true), [])
// Save the current document as a JSON string
const handleSave = useCallback(() => {
const record = JSON.stringify(editor.getDocJSON())
setRecords((records) => [...records, record])
setHasUnsavedChange(false)
}, [editor])
// Load a document from a JSON string
const handleLoad = useCallback((record: string) => {
setDefaultContent(JSON.parse(record))
setHasUnsavedChange(false)
setKey((key) => key + 1)
}, [])
return (
<div className='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 shadow dark:border-zinc-700 flex flex-col bg-white dark:bg-gray-950'>
<button
onClick={handleSave}
disabled={!hasUnsavedChange}
className="m-1 border border-solid bg-white px-2 py-1 text-sm text-black disabled:cursor-not-allowed disabled:text-gray-500"
>
{hasUnsavedChange ? 'Save' : 'No changes to save'}
</button>
<ul className="border-b border-t border-solid text-sm">
{records.map((record, index) => (
<li key={index} className="m-1 flex gap-2">
<button
className="border border-solid bg-white px-2 py-1 text-black"
onClick={() => handleLoad(record)}
>
Load
</button>
<span className="flex-1 overflow-x-scroll p-2">
<pre>{record}</pre>
</span>
</li>
))}
</ul>
<EditorComponent
key={key}
editor={editor}
onDocChange={handleDocChange}
/>
</div>
)
}
<script lang="ts">
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import type { Editor } from 'prosekit/core'
import {
ProseKit,
useDocChange,
} from 'prosekit/svelte'
export let editor: Editor
export let onDocChange: () => void
useDocChange(
() => {
onDocChange?.()
},
{ editor },
)
const mount = (element: HTMLElement) => {
editor.mount(element)
return { destroy: () => editor.unmount() }
}
</script>
<ProseKit {editor}>
<div class='relative w-full flex-1 box-border overflow-y-scroll'>
<div use:mount class='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>
<script lang="ts">
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import EditorComponent from './editor-component.svelte'
import { defineBasicExtension } from 'prosekit/basic'
import {
createEditor,
type NodeJSON,
} from 'prosekit/core'
let defaultContent: NodeJSON | undefined
let hasUnsavedChange = false
let records: string[] = []
let key = 1
// Create a new editor instance whenever `defaultContent` changes
$: editor = createEditor({ extension: defineBasicExtension(), defaultContent })
// Enable the save button
function handleDocChange() {
hasUnsavedChange = true
}
// Save the current document as a JSON string
function handleSave() {
const record = JSON.stringify(editor.getDocJSON())
records = [...records, record]
hasUnsavedChange = false
}
// Load a document from a JSON string
function handleLoad(record: string) {
defaultContent = JSON.parse(record)
key++
hasUnsavedChange = false
}
</script>
<div class='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 shadow dark:border-zinc-700 flex flex-col bg-white dark:bg-gray-950'>
<button
on:click={handleSave}
disabled={!hasUnsavedChange}
class="m-1 border border-solid bg-white px-2 py-1 text-sm text-black disabled:cursor-not-allowed disabled:text-gray-500"
>
{hasUnsavedChange ? 'Save' : 'No changes to save'}
</button>
<ul class="border-b border-t border-solid text-sm">
{#each records as record}
<li class="m-1 flex gap-2">
<button
class="border border-solid bg-white px-2 py-1 text-black"
on:click={() => handleLoad(record)}
>
Load
</button>
<span class="flex-1 overflow-x-scroll p-2">
<pre>{record}</pre>
</span>
</li>
{/each}
</ul>
{#key key}
<EditorComponent {editor} onDocChange={handleDocChange} />
{/key}
</div>
<script setup lang="ts">
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import type { Editor } from 'prosekit/core'
import {
ProseKit,
useDocChange,
} from 'prosekit/vue'
import {
ref,
watchPostEffect,
} from 'vue'
const props = defineProps<{
editor: Editor
}>()
const emit = defineEmits<{
docChange: []
}>()
useDocChange(
() => {
emit('docChange')
},
{ editor: props.editor },
)
const editorRef = ref<HTMLDivElement | null>(null)
watchPostEffect((onCleanup) => {
const editor = props.editor
editor.mount(editorRef.value)
onCleanup(() => editor.unmount())
})
</script>
<template>
<ProseKit :editor="editor">
<div class='relative w-full flex-1 box-border overflow-y-scroll'>
<div ref="editorRef" class='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>
</ProseKit>
</template>
<script setup lang="ts">
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import { defineBasicExtension } from 'prosekit/basic'
import {
createEditor,
type NodeJSON,
} from 'prosekit/core'
import {
computed,
ref,
} from 'vue'
import EditorComponent from './editor-component.vue'
const defaultContent = ref<NodeJSON | undefined>()
const records = ref<string[]>([])
const hasUnsavedChange = ref(false)
const key = ref(1)
// Create a new editor instance whenever `defaultContent` changes
const editor = computed(() => {
const extension = defineBasicExtension()
return createEditor({ extension, defaultContent: defaultContent.value })
})
// Enable the save button
const handleDocChange = () => (hasUnsavedChange.value = true)
// Save the current document as a JSON string
function handleSave() {
const record = JSON.stringify(editor.value.getDocJSON())
records.value.push(record)
hasUnsavedChange.value = false
}
// Load a document from a JSON string
function handleLoad(record: string) {
defaultContent.value = JSON.parse(record)
hasUnsavedChange.value = false
key.value++
}
</script>
<template>
<div class='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 shadow dark:border-zinc-700 flex flex-col bg-white dark:bg-gray-950'>
<button
:disabled="!hasUnsavedChange"
class="m-1 border border-solid bg-white px-2 py-1 text-sm text-black disabled:cursor-not-allowed disabled:text-gray-500"
@click="handleSave"
>
{{ hasUnsavedChange ? 'Save' : 'No changes to save' }}
</button>
<ul class="border-b border-t border-solid text-sm">
<li
v-for="(record, index) in records"
:key="index"
class="m-1 flex gap-2"
>
<button
class="border border-solid bg-white px-2 py-1 text-black"
@click="handleLoad(record)"
>
Load
</button>
<span class="flex-1 overflow-x-scroll p-2">
<pre>{{ record }}</pre>
</span>
</li>
</ul>
<EditorComponent
:key="key"
:editor="editor"
@doc-change="handleDocChange"
/>
</div>
</template>
Working with HTML
HTML is useful when you need to display your content outside the editor or integrate with systems that understand HTML.
Saving Content as HTML
To convert your document to HTML:
// Get the current document as an HTML string
const html = editor.getDocHTML()
// Store the HTML
localStorage.setItem('my-document-html', html)
Loading Content from HTML
To load content from HTML:
// Retrieve stored HTML
const html = localStorage.getItem('my-document-html')
// Create editor with the loaded HTML
const editor = createEditor({
extension,
defaultContent: html, // Pass the HTML string
})
HTML Example
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>
)
}
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import { defineBasicExtension } from 'prosekit/basic'
import {
createEditor,
jsonFromHTML,
type NodeJSON,
} from 'prosekit/core'
import {
useCallback,
useMemo,
useState,
} from 'react'
import EditorComponent from './editor-component'
export default function Editor() {
const [defaultContent, setDefaultContent] = useState<NodeJSON | undefined>()
const [records, setRecords] = useState<string[]>([])
const [hasUnsavedChange, setHasUnsavedChange] = useState(false)
const [key, setKey] = useState(1)
// Create a new editor instance whenever `defaultContent` changes
const editor = useMemo(() => {
const extension = defineBasicExtension()
return createEditor({ extension, defaultContent })
}, [defaultContent])
// Enable the save button
const handleDocChange = useCallback(() => setHasUnsavedChange(true), [])
// Save the current document as a HTML string
const handleSave = useCallback(() => {
const record = editor.getDocHTML()
setRecords((records) => [...records, record])
setHasUnsavedChange(false)
}, [editor])
// Load a document from a HTML string
const handleLoad = useCallback(
(record: string) => {
setDefaultContent(jsonFromHTML(record, { schema: editor.schema }))
setHasUnsavedChange(false)
setKey((key) => key + 1)
},
[editor.schema],
)
return (
<div className='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 shadow dark:border-zinc-700 flex flex-col bg-white dark:bg-gray-950'>
<button
onClick={handleSave}
disabled={!hasUnsavedChange}
className="m-1 border border-solid bg-white px-2 py-1 text-sm text-black disabled:cursor-not-allowed disabled:text-gray-500"
>
{hasUnsavedChange ? 'Save' : 'No changes to save'}
</button>
<ul className="border-b border-t border-solid text-sm">
{records.map((record, index) => (
<li key={index} className="m-1 flex gap-2">
<button
className="border border-solid bg-white px-2 py-1 text-black"
onClick={() => handleLoad(record)}
>
Load
</button>
<span className="flex-1 overflow-x-scroll p-2">
<pre>{record}</pre>
</span>
</li>
))}
</ul>
<EditorComponent
key={key}
editor={editor}
onDocChange={handleDocChange}
/>
</div>
)
}
<script setup lang="ts">
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import type { Editor } from 'prosekit/core'
import {
ProseKit,
useDocChange,
} from 'prosekit/vue'
import {
ref,
watchPostEffect,
} from 'vue'
const props = defineProps<{
editor: Editor
}>()
const emit = defineEmits<{
docChange: []
}>()
useDocChange(
() => {
emit('docChange')
},
{ editor: props.editor },
)
const editorRef = ref<HTMLDivElement | null>(null)
watchPostEffect((onCleanup) => {
const editor = props.editor
editor.mount(editorRef.value)
onCleanup(() => editor.unmount())
})
</script>
<template>
<ProseKit :editor="editor">
<div class='relative w-full flex-1 box-border overflow-y-scroll'>
<div ref="editorRef" class='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>
</ProseKit>
</template>
<script setup lang="ts">
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import { defineBasicExtension } from 'prosekit/basic'
import {
createEditor,
jsonFromHTML,
type NodeJSON,
} from 'prosekit/core'
import {
computed,
ref,
} from 'vue'
import EditorComponent from './editor-component.vue'
const defaultContent = ref<NodeJSON | undefined>()
const records = ref<string[]>([])
const hasUnsavedChange = ref(false)
const key = ref(1)
// Create a new editor instance whenever `defaultContent` changes
const editor = computed(() => {
const extension = defineBasicExtension()
return createEditor({ extension, defaultContent: defaultContent.value })
})
// Enable the save button
const handleDocChange = () => (hasUnsavedChange.value = true)
// Save the current document as a HTML string
function handleSave() {
const record = editor.value.getDocHTML()
records.value.push(record)
hasUnsavedChange.value = false
}
// Load a document from a HTML string
function handleLoad(record: string) {
defaultContent.value = jsonFromHTML(record, { schema: editor.value.schema })
hasUnsavedChange.value = false
key.value++
}
</script>
<template>
<div class='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 shadow dark:border-zinc-700 flex flex-col bg-white dark:bg-gray-950'>
<button
:disabled="!hasUnsavedChange"
class="m-1 border border-solid bg-white px-2 py-1 text-sm text-black disabled:cursor-not-allowed disabled:text-gray-500"
@click="handleSave"
>
{{ hasUnsavedChange ? 'Save' : 'No changes to save' }}
</button>
<ul class="border-b border-t border-solid text-sm">
<li
v-for="(record, index) in records"
:key="index"
class="m-1 flex gap-2"
>
<button
class="border border-solid bg-white px-2 py-1 text-black"
@click="handleLoad(record)"
>
Load
</button>
<span class="flex-1 overflow-x-scroll p-2">
<pre>{{ record }}</pre>
</span>
</li>
</ul>
<EditorComponent
:key="key"
:editor="editor"
@doc-change="handleDocChange"
/>
</div>
</template>
Working with Markdown
While ProseKit doesn’t directly support Markdown as a storage format, you can add Markdown support using additional libraries.
Markdown Example
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>
)
}
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import { defineBasicExtension } from 'prosekit/basic'
import {
createEditor,
jsonFromHTML,
type NodeJSON,
} from 'prosekit/core'
import {
useCallback,
useMemo,
useState,
} from 'react'
import EditorComponent from './editor-component'
import {
htmlFromMarkdown,
markdownFromHTML,
} from './markdown'
export default function Editor() {
const [defaultContent, setDefaultContent] = useState<NodeJSON | undefined>()
const [records, setRecords] = useState<string[]>([])
const [hasUnsavedChange, setHasUnsavedChange] = useState(false)
const [key, setKey] = useState(1)
// Create a new editor instance whenever `defaultContent` changes
const editor = useMemo(() => {
const extension = defineBasicExtension()
return createEditor({ extension, defaultContent })
}, [defaultContent])
// Enable the save button
const handleDocChange = useCallback(() => setHasUnsavedChange(true), [])
// Save the current document as a Markdown string
const handleSave = useCallback(() => {
const html = editor.getDocHTML()
const record = markdownFromHTML(html)
setRecords((records) => [...records, record])
setHasUnsavedChange(false)
}, [editor])
// Load a document from a Markdown string
const handleLoad = useCallback(
(record: string) => {
const html = htmlFromMarkdown(record)
setDefaultContent(jsonFromHTML(html, { schema: editor.schema }))
setHasUnsavedChange(false)
setKey((key) => key + 1)
},
[editor.schema],
)
return (
<div className='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 shadow dark:border-zinc-700 flex flex-col bg-white dark:bg-gray-950'>
<button
onClick={handleSave}
disabled={!hasUnsavedChange}
className="m-1 border border-solid bg-white px-2 py-1 text-sm text-black disabled:cursor-not-allowed disabled:text-gray-500"
>
{hasUnsavedChange ? 'Save' : 'No changes to save'}
</button>
<ul className="border-b border-t border-solid text-sm">
{records.map((record, index) => (
<li key={index} className="m-1 flex gap-2">
<button
className="border border-solid bg-white px-2 py-1 text-black"
onClick={() => handleLoad(record)}
>
Load
</button>
<span className="flex-1 overflow-x-scroll p-2">
<pre>{record}</pre>
</span>
</li>
))}
</ul>
<EditorComponent
key={key}
editor={editor}
onDocChange={handleDocChange}
/>
</div>
)
}
import rehypeParse from 'rehype-parse'
import rehypeRemark from 'rehype-remark'
import remarkHtml from 'remark-html'
import remarkParse from 'remark-parse'
import remarkStringify from 'remark-stringify'
import { unified } from 'unified'
export function markdownFromHTML(html: string): string {
return unified()
.use(rehypeParse)
.use(rehypeRemark)
.use(remarkStringify)
.processSync(html)
.toString()
}
export function htmlFromMarkdown(markdown: string): string {
return unified()
.use(remarkParse)
.use(remarkHtml)
.processSync(markdown)
.toString()
}
<script setup lang="ts">
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import type { Editor } from 'prosekit/core'
import {
ProseKit,
useDocChange,
} from 'prosekit/vue'
import {
ref,
watchPostEffect,
} from 'vue'
const props = defineProps<{
editor: Editor
}>()
const emit = defineEmits<{
docChange: []
}>()
useDocChange(
() => {
emit('docChange')
},
{ editor: props.editor },
)
const editorRef = ref<HTMLDivElement | null>(null)
watchPostEffect((onCleanup) => {
const editor = props.editor
editor.mount(editorRef.value)
onCleanup(() => editor.unmount())
})
</script>
<template>
<ProseKit :editor="editor">
<div class='relative w-full flex-1 box-border overflow-y-scroll'>
<div ref="editorRef" class='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>
</ProseKit>
</template>
<script setup lang="ts">
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import { defineBasicExtension } from 'prosekit/basic'
import {
createEditor,
jsonFromHTML,
type NodeJSON,
} from 'prosekit/core'
import {
computed,
ref,
} from 'vue'
import EditorComponent from './editor-component.vue'
import {
htmlFromMarkdown,
markdownFromHTML,
} from './markdown'
const defaultContent = ref<NodeJSON | undefined>()
const records = ref<string[]>([])
const hasUnsavedChange = ref(false)
const key = ref(1)
// Create a new editor instance whenever `defaultContent` changes
const editor = computed(() => {
const extension = defineBasicExtension()
return createEditor({ extension, defaultContent: defaultContent.value })
})
// Enable the save button
const handleDocChange = () => (hasUnsavedChange.value = true)
// Save the current document as a Markdown string
function handleSave() {
const html = editor.value.getDocHTML()
const record = markdownFromHTML(html)
records.value.push(record)
hasUnsavedChange.value = false
}
// Load a document from a Markdown string
function handleLoad(record: string) {
const html = htmlFromMarkdown(record)
defaultContent.value = jsonFromHTML(html, { schema: editor.value.schema })
hasUnsavedChange.value = false
key.value++
}
</script>
<template>
<div class='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 shadow dark:border-zinc-700 flex flex-col bg-white dark:bg-gray-950'>
<button
:disabled="!hasUnsavedChange"
class="m-1 border border-solid bg-white px-2 py-1 text-sm text-black disabled:cursor-not-allowed disabled:text-gray-500"
@click="handleSave"
>
{{ hasUnsavedChange ? 'Save' : 'No changes to save' }}
</button>
<ul class="border-b border-t border-solid text-sm">
<li
v-for="(record, index) in records"
:key="index"
class="m-1 flex gap-2"
>
<button
class="border border-solid bg-white px-2 py-1 text-black"
@click="handleLoad(record)"
>
Load
</button>
<span class="flex-1 overflow-x-scroll p-2">
<pre>{{ record }}</pre>
</span>
</li>
</ul>
<EditorComponent
:key="key"
:editor="editor"
@doc-change="handleDocChange"
/>
</div>
</template>
import rehypeParse from 'rehype-parse'
import rehypeRemark from 'rehype-remark'
import remarkHtml from 'remark-html'
import remarkParse from 'remark-parse'
import remarkStringify from 'remark-stringify'
import { unified } from 'unified'
export function markdownFromHTML(html: string): string {
return unified()
.use(rehypeParse)
.use(rehypeRemark)
.use(remarkStringify)
.processSync(html)
.toString()
}
export function htmlFromMarkdown(markdown: string): string {
return unified()
.use(remarkParse)
.use(remarkHtml)
.processSync(markdown)
.toString()
}
Conversion Utilities
ProseKit provides utility functions for converting between plain JSON object, HTML string, and ProseMirrorNode
.