---
title: Migrate from TinyMCE to Jodit
description: Step-by-step TinyMCE to Jodit migration with before/after code: init, content and events, toolbar, custom buttons, plugins, uploads, a plugin map and themes.
keywords: tinymce to jodit, migrate from tinymce, tinymce alternative, replace tinymce, tinymce plugin equivalent, tinymce 7 gpl migration
---

# Migrate from TinyMCE to Jodit

Both editors edit HTML in a `contenteditable`, so your stored content does not change. What changes is the configuration object, the toolbar names and the plugin API. This guide shows each piece side by side.

```tldr
Content: HTML in, HTML out in both editors, no conversion
Init: `tinymce.init({ selector })` becomes `Jodit.make(selector, options)`
Toolbar: a space-separated string becomes a `buttons` array
Plugins: `tinymce.PluginManager.add` becomes `Jodit.plugins.add`
License: the Jodit core is MIT, no `license_key` option
Compare: [Jodit vs TinyMCE](/jodit/vs/tinymce/) for prices and features
```

## 1. Install

````beforeafter
### TinyMCE

```bash
npm install tinymce @tinymce/tinymce-react
```

### Jodit

```bash
npm uninstall tinymce @tinymce/tinymce-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
### TinyMCE

```js
import tinymce from 'tinymce';
import 'tinymce/themes/silver';
import 'tinymce/plugins/link';
import 'tinymce/plugins/image';
import 'tinymce/plugins/table';

tinymce.init({
	selector: '#editor',
	license_key: 'gpl',
	plugins: 'link image table lists',
	toolbar: 'undo redo | bold italic | bullist numlist | link image table',
	menubar: false,
	height: 400,
	placeholder: 'Start typing'
});
```

### Jodit

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

const editor = Jodit.make('#editor', {
	buttons: ['undo', 'redo', '|', 'bold', 'italic', '|', 'ul', 'ol', '|', 'link', 'image', 'table'],
	height: 400,
	placeholder: 'Start typing'
});
```
````

Jodit has no menubar and no theme bundle to import: the stylesheet is one file and every core plugin is in the main build. `Jodit.make` returns the instance right away, so you do not wait for a promise or look it up through `tinymce.get()`.

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

````beforeafter
### TinyMCE

```js
const editor = tinymce.get('editor');

const html = editor.getContent();
editor.setContent('<p>Hello</p>');

editor.on('change', () => save(editor.getContent()));
editor.on('blur', () => validate());

editor.mode.set('readonly');
editor.remove();
```

### Jodit

```js
const editor = Jodit.make('#editor');

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

editor.events.on('change', newValue => save(newValue));
editor.events.on('blur', () => validate());

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

`change` in Jodit fires with the new HTML as the first argument, so you rarely need to read `value` inside the handler. Common events map one to one: `init` is `afterInit`, `focus` and `blur` keep their names, `keydown`, `paste` and `click` are DOM event names.

## 4. Toolbar

TinyMCE describes the toolbar as a string; Jodit uses an array. Groups are `|` in both.

````beforeafter
### TinyMCE

```js
tinymce.init({
	toolbar: 'undo redo | blocks fontfamily fontsize | bold italic underline strikethrough | forecolor backcolor | alignleft aligncenter alignright | bullist numlist outdent indent | link image media table | code fullscreen',
	toolbar_mode: 'sliding'
});
```

### Jodit

```js
Jodit.make('#editor', {
	buttons: [
		'undo', 'redo', '|',
		'paragraph', 'font', 'fontsize', '|',
		'bold', 'italic', 'underline', 'strikethrough', '|',
		'brush', '|',
		'left', 'center', 'right', '|',
		'ul', 'ol', 'outdent', 'indent', '|',
		'link', 'image', 'video', 'table', '|',
		'source', 'fullsize'
	],
	toolbarAdaptive: false
});
```
````

Jodit picks a layout per editor width (`buttons`, `buttonsMD`, `buttonsSM`, `buttonsXS`). Set `toolbarAdaptive: false` to use one list everywhere, or fill all four for full control. `removeButtons: ['video']` drops a button from the defaults without listing the rest; `extraButtons` appends.

Button names that differ:

| TinyMCE                                                  | Jodit                                                          |
| -------------------------------------------------------- | -------------------------------------------------------------- |
| `blocks`                                                 | `paragraph`                                                    |
| `fontfamily`, `fontsize`                                 | `font`, `fontsize`                                             |
| `forecolor`, `backcolor`                                 | `brush` (one button, two tabs)                                 |
| `alignleft`, `aligncenter`, `alignright`, `alignjustify` | `left`, `center`, `right`, `justify` (or one `align` dropdown) |
| `bullist`, `numlist`                                     | `ul`, `ol`                                                     |
| `media`                                                  | `video`                                                        |
| `code`                                                   | `source`                                                       |
| `fullscreen`                                             | `fullsize`                                                     |
| `removeformat`                                           | `eraser`                                                       |
| `charmap`                                                | `symbols`                                                      |
| `searchreplace`                                          | `find`                                                         |
| `preview`, `print`                                       | `preview`, `print`                                             |
| `hr`                                                     | `hr`                                                           |
| `emoticons`                                              | `emoji` (PRO)                                                  |

## 5. A custom button

````beforeafter
### TinyMCE

```js
tinymce.init({
	selector: '#editor',
	toolbar: 'bold italic | insertDate',
	setup(editor) {
		editor.ui.registry.addButton('insertDate', {
			text: 'Date',
			tooltip: 'Insert current date',
			onAction() {
				editor.insertContent(new Date().toDateString());
			}
		});
	}
});
```

### Jodit

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

To reuse a button across editors, register it once in `Jodit.defaultOptions.controls.insertDate = { ... }` and refer to it by name. Dropdowns use `list`, toggles use `isActive`, icons are SVG strings or a name from the [icon set](/jodit/docs/modules/icons.html). The full walkthrough: [add a custom button](/jodit/pro/docs/how-to/add-custom-button.md).

## 6. Writing a plugin

````beforeafter
### TinyMCE

```js
tinymce.PluginManager.add('wordcounter', editor => {
	let el;

	editor.on('init', () => {
		el = document.createElement('div');
		editor.getContainer().append(el);
	});

	editor.on('change keyup', () => {
		const words = editor.getContent({ format: 'text' }).trim().split(/\s+/).length;
		el.textContent = `${words} words`;
	});

	return { getMetadata: () => ({ name: 'Word counter' }) };
});

tinymce.init({ selector: '#editor', plugins: 'wordcounter' });
```

### Jodit

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

	editor.events
		.on('afterInit', () => editor.container.append(el))
		.on('change keyup', () => {
			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`) that receives the editor instance; it is active in every editor unless you list it in `disablePlugins`. Several events can be bound in one call, and `editor.editor` is the editable element. Classes can declare their own `buttons` and `requires`. See [how to create a plugin](/jodit/pro/docs/how-to/create-plugin.md).

## 7. Image upload

````beforeafter
### TinyMCE

```js
tinymce.init({
	selector: '#editor',
	plugins: 'image',
	images_upload_url: '/upload',
	images_upload_credentials: true,
	file_picker_types: 'image'
});
```

### Jodit

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

TinyMCE expects `{ "location": "/path.jpg" }` back; the Jodit uploader expects `{ "success": true, "data": { "baseurl": "/files/", "files": ["path.jpg"] } }`. Keep your endpoint and adapt the response with `uploader.process`, or install one of the connectors ([Node.js](/jodit/examples/integration/jodit-nodejs.html), [Python](/jodit/examples/integration/jodit-python.html), PHP), which also power the file browser.

## 8. Plugin map

| TinyMCE plugin                    | Jodit                                                | Where                   |
| --------------------------------- | ---------------------------------------------------- | ----------------------- |
| `lists`, `advlist`                | `ordered-list`, `indent`                             | core                    |
| `link`, `autolink`                | `link`                                               | core                    |
| `image`, `imagetools`             | `image`, `image-properties`, `resizer`, image editor | core                    |
| `media`                           | `video`, `media`                                     | core                    |
| `table`                           | `table`, `select-cells`, `resize-cells`              | core                    |
| `code`                            | `source`                                             | core                    |
| `codesample`                      | `paste-code`                                         | PRO                     |
| `paste`                           | `paste`, `paste-storage`                             | core                    |
| PowerPaste                        | `paste-from-word`                                    | core, enhanced in PRO   |
| `searchreplace`                   | `search`                                             | core                    |
| `wordcount`                       | `stat`, `limit`                                      | core                    |
| `fullscreen`                      | `fullsize`                                           | core                    |
| `preview`, `print`                | `preview`, `print`                                   | core                    |
| `charmap`                         | `symbols`                                            | core                    |
| `emoticons`                       | `emoji`                                              | PRO                     |
| `template`                        | `templates`                                          | PRO                     |
| `checklist`                       | `todo-list`                                          | PRO                     |
| `casechange`                      | `change-case`                                        | PRO                     |
| `mentions`                        | `autocomplete`                                       | PRO                     |
| `tinymcespellchecker`             | `spellcheck` (browser)                               | core                    |
| `export`, `importword`            | `export-docs`                                        | PRO                     |
| `ai`                              | `ai-assistant`                                       | core, PRO adds adapters |
| `autosave`                        | `backup`                                             | PRO                     |
| `pageembed`                       | `iframe-editor`                                      | PRO                     |
| `quickbars`                       | `inline-popup`                                       | core                    |
| `visualblocks`                    | `show-blocks`                                        | PRO                     |
| Revision History, Suggested Edits | [Jodit Collaboration](/jodit/pro/collaboration/)     | separate product        |

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

## 9. Skins and themes

````beforeafter
### TinyMCE

```js
tinymce.init({
	selector: '#editor',
	skin: 'oxide-dark',
	content_css: 'dark'
});
```

### Jodit

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

```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 styling: TinyMCE's `content_style` becomes `iframeStyle` when you run Jodit in iframe mode (`iframe: true`), and plain page CSS otherwise, because by default Jodit edits inline in your page. See [UI themes](/jodit/examples/theme/ui-themes.html) and [dark theme](/jodit/examples/theme/dark.html).

## 10. Option names you will look for

| TinyMCE                    | Jodit                                                          |
| -------------------------- | -------------------------------------------------------------- |
| `height`, `min_height`     | `height`, `minHeight`                                          |
| `placeholder`              | `placeholder`                                                  |
| `language`                 | `language`                                                     |
| `readonly`                 | `readonly`                                                     |
| `inline`                   | `inline`                                                       |
| `toolbar_sticky`           | `toolbarSticky` (default on)                                   |
| `content_style`            | `iframeStyle` with `iframe: true`                              |
| `paste_as_text`            | `defaultActionOnPaste: 'insert_only_text'`                     |
| `entity_encoding: 'raw'`   | default behaviour                                              |
| `forced_root_block: 'div'` | `enter: 'div'`                                                 |
| `branding: false`          | `disablePlugins: ['powered-by-jodit']`                         |
| `statusbar: false`         | `showCharsCounter`, `showWordsCounter`, `showXPathInStatusbar` |

## Checklist

```steps
### Swap the init call
Replace `tinymce.init` with `Jodit.make`, move `selector` to the first argument and `toolbar` into `buttons`.

### Wire content and events
`getContent`/`setContent` become `editor.value`; `editor.on` becomes `editor.events.on`.

### Port custom buttons and plugins
`addButton` becomes a control object; `PluginManager.add` becomes `Jodit.plugins.add`.

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

### Check the output
Paste your three most complex documents into the [playground](/jodit/play.html) and compare the HTML.
```

## Links

```cards
- [Jodit vs TinyMCE](/jodit/vs/tinymce/): License, pricing and feature comparison.
- [Options reference](/jodit/docs/options.html): Every Jodit option with defaults.
- [Builder](/jodit/builder/): Pick plugins and buttons and get the config and a smaller bundle.
- [React](/jodit/integrations/react/): jodit-react, the drop-in for tinymce-react.
```
