Example: save-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 dark:border-gray-700 shadow flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white'> <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 dark:border-gray-700 shadow flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white'> <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>