Skip to main content

SimpleMDE Alternative: The Diff Is Smaller Than You Think

Screen-print illustration of a dusty abandoned panel beside a working one, a single cable being unplugged from the first and seated into the second

SimpleMDE's last commit was June 2024. It has 290 open issues and 10,141 stars, which is an awkward combination: enough people depend on it that it still does 94,000 installs a month, and nobody is answering any of them.

If you're on it, you've probably already decided to move. This is the part nobody writes down: what the migration actually costs.

The short answer is that it's smaller than a normal migration, because you spend most of it deleting code.

The one line you delete

SimpleMDE replaces your textarea with a CodeMirror instance. The textarea stays in the DOM but nothing types into it, which is why almost every SimpleMDE codebase has some version of this:

form.addEventListener('submit', () => {
  document.querySelector('#content').value = simplemde.value();
});

That line exists because the editor and the form disagree about where the content lives.

markdown-text-editor doesn't replace the textarea. It wraps it and puts a toolbar on top, and you type into the real element. So:

import MarkdownEditor from 'markdown-text-editor';

new MarkdownEditor('#content');

The sync handler goes. So does any simplemde.value() call elsewhere in your code, because document.querySelector('#content').value is now simply correct, at every moment, without anyone doing anything.

If you kept a required attribute on that textarea and quietly wondered why it never fired, it fires now.

Install

npm uninstall simplemde
npm install markdown-text-editor

Two dependencies, marked and dompurify. No CodeMirror.

Options

The constructor shape differs: SimpleMDE takes the element inside the options object, this takes a selector or element first and options second.

SimpleMDEmarkdown-text-editor
element: elfirst argument: new MarkdownEditor(el)
initialValueset the textarea's content as normal
placeholderplaceholder
toolbartoolbar
statusfooter, or false to hide it
renderingConfigrenderer, a function returning HTML
spellCheckerno equivalent, see below
autosaveno equivalent, see below

So a typical config:

new MarkdownEditor('#content', {
  placeholder: 'Write something…',
  toolbar: ['bold', 'italic', 'link', 'code', 'preview'],
  footer: { words: true, chars: true },
});

Toolbar names

Most buttons exist under a different name. This is the whole mapping:

SimpleMDEmarkdown-text-editor
bold, italic, strikethroughsame
code, table, link, imagesame
previewpreview (fullscreen, side by side)
quoteblockquote
unordered-listul
ordered-listol
horizontal-rulehr
heading, heading-1/2/3heading, with a levels option
side-by-side, fullscreenfolded into preview
clean-block, guideno equivalent

Headings are configurable rather than fixed, which is useful on a CMS body field where H1 belongs to the page:

toolbar: [{ heading: { levels: [2, 3, 4] } }, 'bold', 'italic', 'preview']

You also get checklist, codeblock, undo, redo, indent and outdent, which SimpleMDE didn't have.

What you lose

Three things, and it's better to know now than after you've migrated.

The spell checker. SimpleMDE shipped one. There's no equivalent here. In practice most projects disabled it, because it fought the browser's own spell check and flagged markdown syntax as misspellings, but if you were relying on it this is a real gap.

Autosave to localStorage. SimpleMDE had it built in. Here you'd wire it yourself through the onChange callback, which is a few lines rather than a feature.

The guide button. The one linking to markdown syntax help. Not included.

Everything else has an equivalent, including fullscreen and side-by-side preview, which are the same button.

If you're on EasyMDE instead

EasyMDE is the maintained fork and does nearly a million installs a month, so "it's abandoned" doesn't apply to it. The migration is almost identical, since the API barely diverged.

But the reason to move is different. It isn't maintenance, it's that EasyMDE also replaces the textarea, so you keep the sync code and everything that reads the form early still reads an empty string: FormData, htmx, Turbo, unsaved-changes guards, server-side validation. If none of that has ever bitten you, EasyMDE is a perfectly good editor and you should stay on it.

Worth half an hour

The realistic migration is: change the constructor, rename four toolbar entries, delete the sync handler, re-test the form.

If you hit something this guide doesn't cover, open an issue — unlike the repo you're leaving, someone reads them.

Full documentation · framework guides for Django, Laravel and plain JavaScript.