---
title: Migrate from Tiptap to Jodit
description: Tiptap to Jodit migration with before/after code: new Editor with extensions vs Jodit.make, getHTML and setContent, onUpdate, a hand-built toolbar vs buttons, custom extensions vs controls and plugins, uploads, an extension map and styling.
keywords: tiptap to jodit, migrate from tiptap, tiptap alternative, replace tiptap, tiptap extension equivalent, prosemirror to wysiwyg
---

# Migrate from Tiptap to Jodit

Tiptap is a headless framework: you assemble extensions and build every toolbar button yourself. Jodit is a complete editor with a toolbar, dialogs and plugins. Migrating mostly means deleting UI code and replacing the extension list with a `buttons` array.

```tldr
Content: export ProseMirror JSON to HTML once; Jodit stores HTML and keeps markup the schema dropped
Init: `new Editor({ element, extensions })` becomes `Jodit.make(selector, options)`
Toolbar: your React or vanilla buttons become a `buttons` array
Extensions: most StarterKit features are core Jodit plugins; custom extensions become controls or plugins
License: MIT in both; Jodit needs no cloud plan for uploads or files
Compare: [Jodit vs Tiptap](/jodit/vs/tiptap/) for the approach and prices
```

## 1. Install

````beforeafter
### Tiptap

```bash
npm install @tiptap/core @tiptap/pm @tiptap/starter-kit @tiptap/react
```

### Jodit

```bash
npm uninstall @tiptap/core @tiptap/pm @tiptap/starter-kit @tiptap/react
npm install jodit
# React projects: npm install jodit-react
# PRO: npm install jodit-pro jodit-pro-react
```
````

Jodit is one package with no peer dependencies; the React, Angular and Vue wrappers are optional. Without a bundler, two tags replace the whole setup:

```html
<link
	rel="stylesheet"
	href="https://cdnjs.cloudflare.com/ajax/libs/jodit/4.2.27/jodit.min.css"
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jodit/4.2.27/jodit.min.js"></script>
```

## 2. Initialise

````beforeafter
### Tiptap

```js
import { Editor } from '@tiptap/core';
import StarterKit from '@tiptap/starter-kit';
import Link from '@tiptap/extension-link';
import Image from '@tiptap/extension-image';
import { TableKit } from '@tiptap/extension-table';

const editor = new Editor({
	element: document.querySelector('#editor'),
	extensions: [StarterKit, Link, Image, TableKit],
	content: '<p>Hello</p>'
});
```

### Jodit

```js
import { Jodit } from 'jodit';
import 'jodit/es2021/jodit.min.css';

const editor = Jodit.make('#editor', {
	buttons: ['bold', 'italic', 'strikethrough', '|', 'paragraph', '|', 'ul', 'ol', '|', 'link', 'image', 'table']
});

editor.value = '<p>Hello</p>';
```
````

Everything StarterKit provides (bold, italic, strike, code, headings, lists, blockquote, horizontal rule, history) is a core Jodit plugin, and the toolbar comes with it. The [Builder](/jodit/builder/) gives you a smaller bundle if you only want some plugins.

## 3. Read and write content, listen to changes

````beforeafter
### Tiptap

```js
const html = editor.getHTML();
const json = editor.getJSON();
editor.commands.setContent('<p>Hello</p>');

const editor = new Editor({
	onUpdate({ editor }) { save(editor.getHTML()); },
	onBlur() { validate(); },
	onCreate() { console.log('ready'); }
});

editor.setEditable(false);
editor.destroy();
```

### Jodit

```js
const html = editor.value;
editor.value = '<p>Hello</p>';

const editor = Jodit.make('#editor', {
	events: {
		change(newValue) { save(newValue); },
		blur() { validate(); },
		afterInit() { console.log('ready'); }
	}
});

editor.setReadOnly(true);
editor.destruct();
```
````

If your database holds ProseMirror JSON, convert it once with `generateHTML(json, extensions)` from `@tiptap/html` and store the HTML. Jodit has no JSON document model; when the backend needs structure, parse the HTML server-side.

## 4. Toolbar

````beforeafter
### Tiptap (React)

```jsx
function Toolbar({ editor }) {
	return (
		<div className="toolbar">
			<button onClick={() => editor.chain().focus().toggleBold().run()} className={editor.isActive('bold') ? 'is-active' : ''}>B</button>
			<button onClick={() => editor.chain().focus().toggleItalic().run()}>I</button>
			<button onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}>H2</button>
			<button onClick={() => editor.chain().focus().toggleBulletList().run()}>•</button>
			<button onClick={() => editor.chain().focus().setLink({ href: prompt('URL') }).run()}>Link</button>
			<button onClick={() => editor.chain().focus().insertTable({ rows: 3, cols: 3 }).run()}>Table</button>
		</div>
	);
}
```

### Jodit (React)

```jsx
import JoditEditor from 'jodit-react';

const config = {
	buttons: ['bold', 'italic', '|', 'paragraph', '|', 'ul', 'ol', '|', 'link', 'table']
};

<JoditEditor value={content} config={config} onBlur={setContent} />
```
````

Active states, link dialogs, table pickers, tooltips, hotkeys and the mobile layout are built in. Jodit chooses a button list by editor width (`buttons`, `buttonsMD`, `buttonsSM`, `buttonsXS`); `toolbarAdaptive: false` keeps one list. A BubbleMenu corresponds to the inline popup (`popup` option), a FloatingMenu to the `add-new-line` plugin.

## 5. A custom button

````beforeafter
### Tiptap

```js
import { Extension } from '@tiptap/core';

const InsertDate = Extension.create({
	name: 'insertDate',
	addCommands() {
		return {
			insertDate: () => ({ commands }) =>
				commands.insertContent(new Date().toDateString())
		};
	}
});

const editor = new Editor({ extensions: [StarterKit, InsertDate] });

// plus a button in your own toolbar component
<button onClick={() => editor.commands.insertDate()}>Date</button>
```

### Jodit

```js
Jodit.make('#editor', {
	buttons: [
		'bold', 'italic', '|',
		{
			name: 'insertDate',
			text: 'Date',
			tooltip: 'Insert current date',
			exec(editor) {
				editor.s.insertHTML(new Date().toDateString());
			}
		}
	]
});
```
````

The command and the button are one object. Register it in `Jodit.defaultOptions.controls` to reuse it by name; `list` makes a dropdown, `isActive` a toggle. Full guide: [add a custom button](/jodit/pro/docs/how-to/add-custom-button.md).

## 6. Writing a plugin

````beforeafter
### Tiptap

```js
import { Extension } from '@tiptap/core';

const WordCounter = Extension.create({
	name: 'wordCounter',
	onCreate() {
		this.storage.el = document.createElement('div');
		this.editor.options.element.append(this.storage.el);
	},
	onUpdate() {
		const words = this.editor.getText().trim().split(/\s+/).length;
		this.storage.el.textContent = `${words} words`;
	}
});

new Editor({ extensions: [StarterKit, WordCounter] });
```

### Jodit

```js
Jodit.plugins.add('wordCounter', editor => {
	const el = document.createElement('div');

	editor.events
		.on('afterInit', () => editor.container.append(el))
		.on('change', () => {
			const words = editor.editor.textContent.trim().split(/\s+/).length;
			el.textContent = `${words} words`;
		});
});

Jodit.make('#editor');
```
````

A Jodit plugin is a function or a class with `init` and `destruct`. It works on the DOM: no schema, no node views, no ProseMirror plugins. Custom markup (a widget, an embed, a data attribute) is inserted as HTML and kept as is. See [how to create a plugin](/jodit/pro/docs/how-to/create-plugin.md).

## 7. Image upload

````beforeafter
### Tiptap

```js
import { Extension } from '@tiptap/core';
import { Plugin } from '@tiptap/pm/state';

const Upload = Extension.create({
	name: 'upload',
	addProseMirrorPlugins() {
		return [new Plugin({
			props: {
				handleDrop: (view, event) => {
					const file = event.dataTransfer?.files[0];
					if (!file) return false;
					const body = new FormData();
					body.append('file', file);
					fetch('/upload', { method: 'POST', body })
						.then(r => r.json())
						.then(({ url }) => this.editor.chain().focus().setImage({ src: url }).run());
					return true;
				}
			}
		})];
	}
});
```

### Jodit

```js
Jodit.make('#editor', {
	uploader: { url: '/upload' }
});
```
````

Drag and drop, paste from the clipboard, the file dialog, progress and error handling are in the `uploader` module. It expects `{ "success": true, "data": { "baseurl": "/files/", "files": ["path.jpg"] } }`; adapt another response shape in `uploader.process`, or run a connector ([Node.js](/jodit/examples/integration/jodit-nodejs.html), [Python](/jodit/examples/integration/jodit-python.html), PHP) and get a file browser with it.

## 8. Extension map

| Tiptap extension                                   | Jodit                                                | Where                   |
| -------------------------------------------------- | ---------------------------------------------------- | ----------------------- |
| StarterKit: Bold, Italic, Strike, Code             | `bold`                                               | core                    |
| StarterKit: Heading, Paragraph, Blockquote         | `format-block`, `paragraph` button                   | core                    |
| StarterKit: BulletList, OrderedList, ListItem      | `ordered-list`                                       | core                    |
| StarterKit: History                                | `redo-undo`                                          | core                    |
| StarterKit: HorizontalRule, HardBreak              | `hr`, `enter`                                        | core                    |
| Underline                                          | `bold` (underline button)                            | core                    |
| Link                                               | `link`                                               | core                    |
| Image                                              | `image`, `image-properties`, `resizer`, image editor | core                    |
| TableKit (Table, TableRow, TableCell, TableHeader) | `table`, `select-cells`, `resize-cells`              | core                    |
| TextAlign                                          | `justify`                                            | core                    |
| TextStyle, Color, Highlight                        | `color`                                              | core                    |
| FontFamily                                         | `font`                                               | core                    |
| Placeholder                                        | `placeholder`                                        | core                    |
| CharacterCount                                     | `stat`, `limit`                                      | core                    |
| Youtube                                            | `video`                                              | core                    |
| BubbleMenu, FloatingMenu                           | `inline-popup`, `add-new-line`                       | core                    |
| Typography                                         | no equivalent                                        |                         |
| CodeBlockLowlight                                  | `paste-code`                                         | PRO                     |
| TaskList, TaskItem                                 | `todo-list`                                          | PRO                     |
| Mention                                            | `autocomplete`                                       | PRO                     |
| Emoji                                              | `emoji`                                              | PRO                     |
| FileHandler                                        | `uploader`, `drag-and-drop`                          | core                    |
| Export, Import (DOCX)                              | `export-docs`                                        | PRO                     |
| AI Toolkit                                         | `ai-assistant`                                       | core, PRO adds adapters |
| Collaboration, CollaborationCursor, Comments       | [Jodit Collaboration](/jodit/pro/collaboration/)     | separate product        |

The complete lists: [core plugins](/jodit/docs/modules/plugins.html) and [PRO plugins](/jodit/pro/docs/).

## 9. Styling and themes

````beforeafter
### Tiptap

```css
/* you style everything: the toolbar you built and the editable area */
.tiptap {
	min-height: 300px;
	padding: 16px;
	border: 1px solid #ddd;
}
.tiptap h2 { font-size: 1.5rem; }
.tiptap table { border-collapse: collapse; }
```

### Jodit

```js
Jodit.make('#editor', { theme: 'dark', minHeight: 300 });
```

```html
<!-- or one of ten community themes -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jodit-ui-themes@1/themes/nord/nord.all.min.css" />
<script>Jodit.make('#editor', { theme: 'nord' });</script>
```
````

Content styles come from your page, because Jodit edits inline in it; with `iframe: true` use `iframeStyle`. See [UI themes](/jodit/examples/theme/ui-themes.html).

## 10. Option names you will look for

| Tiptap                                 | Jodit                                                  |
| -------------------------------------- | ------------------------------------------------------ |
| `content`                              | `editor.value = html` or the element's initial content |
| `editable: false`                      | `readonly: true`                                       |
| `autofocus`                            | `autofocus`                                            |
| `editorProps.attributes.class`         | `className` option or CSS on `.jodit-wysiwyg`          |
| `injectCSS: false`                     | not needed, one stylesheet                             |
| `enableInputRules`, `enablePasteRules` | `defaultActionOnPaste`, `askBeforePasteHTML`           |
| `editor.getText()`                     | `editor.text`                                          |
| `editor.isEmpty`                       | `editor.value === ''` (check `getEditorText`)          |

## Checklist

```steps
### Convert stored JSON to HTML
Run `generateHTML` once over existing documents and save the HTML.

### Replace the editor and delete the toolbar code
`Jodit.make` with a `buttons` array covers the UI; remove the custom menu components.

### Port custom extensions
Commands become controls; `onUpdate` logic becomes a plugin listening to `change`.

### Point the uploader at your endpoint
Keep the server, adapt the JSON response or add a connector.

### Check the output
Open your three most complex documents in the [playground](/jodit/play.html); Jodit keeps attributes and tags the schema would reject.
```

## Links

```cards
- [Jodit vs Tiptap](/jodit/vs/tiptap/): Headless framework or finished editor.
- [Options reference](/jodit/docs/options.html): Every Jodit option with defaults.
- [React](/jodit/integrations/react/): jodit-react replaces @tiptap/react plus your toolbar.
- [Vue](/jodit/integrations/vue/): Composition API mount or a community wrapper.
```
