Get Started
Installation
You can install ProseKit using npm
, yarn
, or pnpm
. Choose the command that corresponds to your package manager:
npm install prosekit
yarn add prosekit
pnpm add prosekit
Minimal Examples
ProseKit is compatible with various JavaScript frameworks and vanilla JavaScript. Here are some minimal examples of how to use ProseKit in different frameworks:
import 'prosekit/basic/style.css'
import {
createEditor,
jsonFromNode,
type NodeJSON,
} from 'prosekit/core'
import type { ProseMirrorNode } from 'prosekit/pm/model'
import {
ProseKit,
useDocChange,
} from 'prosekit/react'
import {
useCallback,
useMemo,
} from 'react'
import { defineExtension } from './extension'
export default function Editor({
defaultContent,
onDocUpdate,
}: {
defaultContent?: NodeJSON
onDocUpdate?: (doc: NodeJSON) => void
}) {
const editor = useMemo(() => {
const extension = defineExtension()
return createEditor({ extension, defaultContent })
}, [defaultContent])
const handleDocChange = useCallback(
(doc: ProseMirrorNode) => onDocUpdate?.(jsonFromNode(doc)),
[onDocUpdate],
)
useDocChange(handleDocChange, { editor })
return (
<ProseKit editor={editor}>
<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-neutral-900'>
<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 [&_pre]:text-white [&_pre]:bg-zinc-800'></div>
</div>
</div>
</ProseKit>
)
}
<script setup lang="ts">
import 'prosekit/basic/style.css'
import {
createEditor,
jsonFromNode,
type NodeJSON,
} from 'prosekit/core'
import {
ProseKit,
useDocChange,
} from 'prosekit/vue'
import {
ref,
watchPostEffect,
} from 'vue'
import { defineExtension } from './extension'
const props = defineProps<{
defaultContent?: NodeJSON
}>()
const emit = defineEmits<{
docUpdate: [doc: NodeJSON]
}>()
const extension = defineExtension()
const editor = createEditor({ extension, defaultContent: props.defaultContent })
useDocChange(
(doc) => {
emit('docUpdate', jsonFromNode(doc))
},
{ editor },
)
const editorRef = ref<HTMLDivElement | null>(null)
watchPostEffect((onCleanup) => {
editor.mount(editorRef.value)
onCleanup(() => editor.unmount())
})
</script>
<template>
<ProseKit :editor="editor">
<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-neutral-900'>
<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 [&_pre]:text-white [&_pre]:bg-zinc-800' />
</div>
</div>
</ProseKit>
</template>
import 'prosekit/basic/style.css'
import {
useCallback,
useMemo,
} from 'preact/hooks'
import { defineBasicExtension } from 'prosekit/basic'
import {
createEditor,
jsonFromNode,
type NodeJSON,
} from 'prosekit/core'
import type { ProseMirrorNode } from 'prosekit/pm/model'
import {
ProseKit,
useDocChange,
} from 'prosekit/preact'
export default function Editor(props: {
defaultContent?: NodeJSON
onDocUpdate?: (doc: NodeJSON) => void
}) {
const editor = useMemo(() => {
const extension = defineBasicExtension()
return createEditor({ extension, defaultContent: props.defaultContent })
}, [])
const handleDocChange = useCallback(
(doc: ProseMirrorNode) => props.onDocUpdate?.(jsonFromNode(doc)),
[props.onDocUpdate],
)
useDocChange(handleDocChange, { editor })
return (
<ProseKit editor={editor}>
<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-neutral-900'>
<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 [&_pre]:text-white [&_pre]:bg-zinc-800'></div>
</div>
</div>
</ProseKit>
)
}
<script lang="ts">
import 'prosekit/basic/style.css'
import { defineBasicExtension } from 'prosekit/basic'
import {
createEditor,
jsonFromNode,
type NodeJSON,
} from 'prosekit/core'
import {
ProseKit,
useDocChange,
} from 'prosekit/svelte'
export let defaultContent: NodeJSON | undefined = undefined
export let onDocUpdate: ((doc: NodeJSON) => void) | undefined = undefined
const extension = defineBasicExtension()
const editor = createEditor({ extension, defaultContent })
useDocChange((doc) => onDocUpdate?.(jsonFromNode(doc)), { editor })
const mount = (element: HTMLElement) => {
editor.mount(element)
return { destroy: () => editor.unmount() }
}
</script>
<ProseKit {editor}>
<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-neutral-900'>
<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 [&_pre]:text-white [&_pre]:bg-zinc-800'></div>
</div>
</div>
</ProseKit>
import 'prosekit/basic/style.css'
import { defineBasicExtension } from 'prosekit/basic'
import {
createEditor,
jsonFromNode,
type NodeJSON,
} from 'prosekit/core'
import {
ProseKit,
useDocChange,
} from 'prosekit/solid'
export default function Editor(props: {
defaultContent?: NodeJSON
onDocUpdate?: (doc: NodeJSON) => void
}) {
const extension = defineBasicExtension()
const editor = createEditor({
extension,
defaultContent: props.defaultContent,
})
useDocChange((doc) => props.onDocUpdate?.(jsonFromNode(doc)), { editor })
return (
<ProseKit editor={editor}>
<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-neutral-900'>
<div class='relative w-full flex-1 box-border overflow-y-scroll'>
<div ref={editor.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 [&_pre]:text-white [&_pre]:bg-zinc-800'></div>
</div>
</div>
</ProseKit>
)
}
Let's go through the code to understand what's going on.
Extensions
All customizations in ProseKit are done through extensions. Here we are using the defineBasicExtension
function to return a basic extension that provides the most common features.
const extension = defineBasicExtension()
Editor
The createEditor
function creates an editor instance.
const editor = createEditor({ extension })
You need to mount the editor to the DOM by calling the editor.mount(element)
method. When the editor is unmounted, you should call editor.unmount()
or editor.mount(null)
to clean up the editor. Check out the minimal examples above to see how to mount the editor in different frameworks.
Data Persistence
The editor's document is stored in JSON, using the NodeJSON
format. The <Editor>
component requires two properties: defaultDoc
and onDocChange
. defaultDoc
is the initial content shown when the editor loads. onDocChange
is a callback function that is executed whenever the document changes.
ProseKit also provides utilities for converting the editor's document to and from HTML. Be aware that converting to HTML format may not capture all details. See the examples below to see how to use these tools:
Styling
ProseKit is headless, giving you full control over your editor's appearance. However, to help you in getting started, we offers two basic stylesheets.
import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
The prosekit/basic/style.css
file provides essential styles to ensure the editor displays correctly.
The prosekit/basic/typography.css
file offers basic typography styles for the editor, like margins, paddings, and font sizes. This is optional, and you can exclude it if you wish to use your own styles.