Skip to main content
ProseKit provides framework-agnostic support through the @prosekit/web package, offering web components and vanilla JavaScript utilities for building powerful editors without any framework dependencies.

Installation

npm install prosekit @prosekit/web
No peer dependencies required - works with any or no framework.

Quick Start

Create a basic editor with vanilla JavaScript:
<!DOCTYPE html>
<html>
<head>
  <title>ProseKit Editor</title>
</head>
<body>
  <div id="editor"></div>
  
  <script type="module">
    import { createEditor } from '@prosekit/core'
    import { defineBasicExtension } from '@prosekit/extensions'
    
    const editor = createEditor({
      extension: defineBasicExtension()
    })
    
    const editorElement = document.getElementById('editor')
    editor.mount(editorElement)
  </script>
</body>
</html>

Core API

createEditor

Create an editor instance:
import { createEditor } from '@prosekit/core'
import { defineBasicExtension } from '@prosekit/extensions'

const editor = createEditor({
  extension: defineBasicExtension(),
  defaultContent: '<p>Hello world!</p>'
})

Editor Methods

mount / unmount

Mount the editor to a DOM element:
const element = document.getElementById('editor')
editor.mount(element)

// Later, unmount the editor
editor.unmount()

focus / blur

Manage editor focus:
editor.focus()
editor.blur()

// Check if editor is focused
if (editor.focused) {
  console.log('Editor is focused')
}

getDocJSON / getDocHTML

Get editor content:
// Get content as JSON
const json = editor.getDocJSON()
console.log(json)

// Get content as HTML
const html = editor.getDocHTML()
console.log(html)

setContent

Update editor content:
// Set from HTML
editor.setContent('<p>New content</p>')

// Set from JSON
editor.setContent({
  type: 'doc',
  content: [{
    type: 'paragraph',
    content: [{ type: 'text', text: 'New content' }]
  }]
})

// Set with selection
editor.setContent('<p>New content</p>', 'end')

commands

Execute editor commands:
// Toggle bold
editor.commands.toggleBold()

// Insert text
editor.commands.insertText('Hello')

// Check if command can be executed
if (editor.commands.toggleBold.canExec()) {
  editor.commands.toggleBold()
}

Extension Management

Dynamically add and remove extensions:
import { defineCodeBlock } from '@prosekit/extensions'

// Add extension
const dispose = editor.use(defineCodeBlock())

// Remove extension later
dispose()

Web Components

ProseKit provides ready-to-use web components from @prosekit/web:

Using Web Components

<script type="module">
  import { createEditor } from '@prosekit/core'
  import { defineBasicExtension } from '@prosekit/extensions'
  // Import web components
  import '@prosekit/web/autocomplete'
  import '@prosekit/web/tooltip'
  
  const editor = createEditor({
    extension: defineBasicExtension()
  })
  
  editor.mount(document.getElementById('editor'))
</script>

<prosekit-tooltip-root>
  <prosekit-tooltip-trigger>
    <button>Hover me</button>
  </prosekit-tooltip-trigger>
  <prosekit-tooltip-content>
    Tooltip text
  </prosekit-tooltip-content>
</prosekit-tooltip-root>

Available Web Components

Autocomplete

<prosekit-autocomplete-popover>
  <prosekit-autocomplete-list>
    <prosekit-autocomplete-empty>No results</prosekit-autocomplete-empty>
    <prosekit-autocomplete-item value="javascript">JavaScript</prosekit-autocomplete-item>
    <prosekit-autocomplete-item value="typescript">TypeScript</prosekit-autocomplete-item>
  </prosekit-autocomplete-list>
</prosekit-autocomplete-popover>

Inline Popover

<prosekit-inline-popover>
  <button onclick="editor.commands.toggleBold()">Bold</button>
  <button onclick="editor.commands.toggleItalic()">Italic</button>
</prosekit-inline-popover>

Tooltip

<prosekit-tooltip-root>
  <prosekit-tooltip-trigger>
    <button>Hover me</button>
  </prosekit-tooltip-trigger>
  <prosekit-tooltip-content>
    Tooltip text
  </prosekit-tooltip-content>
</prosekit-tooltip-root>

Popover

<prosekit-popover-root>
  <prosekit-popover-trigger>
    <button>Open</button>
  </prosekit-popover-trigger>
  <prosekit-popover-content>
    Popover content
  </prosekit-popover-content>
</prosekit-popover-root>

Block Handle

<prosekit-block-handle-draggable></prosekit-block-handle-draggable>
<prosekit-block-handle-add></prosekit-block-handle-add>
<prosekit-block-handle-popover>
  <!-- Block options -->
</prosekit-block-handle-popover>

Table Handle

<prosekit-table-handle-root>
  <prosekit-table-handle-column-root>
    <prosekit-table-handle-column-trigger></prosekit-table-handle-column-trigger>
  </prosekit-table-handle-column-root>
  <prosekit-table-handle-row-root>
    <prosekit-table-handle-row-trigger></prosekit-table-handle-row-trigger>
  </prosekit-table-handle-row-root>
</prosekit-table-handle-root>

Resizable

<prosekit-resizable-root>
  <img src="image.jpg" />
  <prosekit-resizable-handle></prosekit-resizable-handle>
</prosekit-resizable-root>

Event Handling

Listen to editor events:
import { defineUpdateHandler, defineMountHandler } from '@prosekit/core'

// Listen to document updates
const updateExtension = defineUpdateHandler(() => {
  console.log('Document updated')
})

editor.use(updateExtension)

// Listen to mount events
const mountExtension = defineMountHandler(() => {
  console.log('Editor mounted')
})

editor.use(mountExtension)

Keyboard Shortcuts

Define custom keyboard shortcuts:
import { defineKeymap } from '@prosekit/core'

const keymapExtension = defineKeymap({
  'Mod-s': (state, dispatch) => {
    console.log('Save triggered')
    // Save document
    return true
  },
  'Mod-b': (state, dispatch) => {
    // Toggle bold
    return editor.commands.toggleBold()
  }
})

editor.use(keymapExtension)

Document Queries

Query and manipulate the document:
// Access ProseMirror state
const state = editor.state
const doc = state.doc
const selection = state.selection

// Get text content
const text = doc.textContent

// Get selected text
const selectedText = state.doc.textBetween(
  selection.from,
  selection.to
)

// Count words
const wordCount = doc.textContent.split(/\s+/).length

TypeScript Support

ProseKit has full TypeScript support:
import { createEditor, type Editor } from '@prosekit/core'
import { defineBasicExtension } from '@prosekit/extensions'

const extension = defineBasicExtension()
type MyExtension = typeof extension

const editor: Editor<MyExtension> = createEditor({ extension })

// editor.commands has full type safety and autocomplete
editor.commands.toggleBold()

Module Bundlers

Vite

// vite.config.js
import { defineConfig } from 'vite'

export default defineConfig({
  // ProseKit works out of the box with Vite
})

Webpack

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  }
}

Rollup

// rollup.config.js
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'

export default {
  plugins: [
    resolve(),
    commonjs()
  ]
}

CDN Usage

Use ProseKit directly from a CDN:
<script type="module">
  import { createEditor } from 'https://esm.sh/@prosekit/core'
  import { defineBasicExtension } from 'https://esm.sh/@prosekit/extensions'
  
  const editor = createEditor({
    extension: defineBasicExtension()
  })
  
  editor.mount(document.getElementById('editor'))
</script>

Browser Support

ProseKit supports all modern browsers:
  • Chrome/Edge >= 90
  • Firefox >= 88
  • Safari >= 14
For older browsers, you may need polyfills for:
  • Promise
  • Set / Map
  • Object.assign

Examples

Check out full working examples:

Next Steps

Extensions

Learn about available extensions

Commands

Explore editor commands

Styling

Customize editor appearance

API Reference

Full API documentation