django-rmeditor
A lightweight, self-hosted rich text editor for Django. It outputs plain HTML,
so it is a drop-in replacement for a TextField / tinymce HTMLField with no data
migration. No CDN, no API key, no branding, no word/usage limits.
- Pure vanilla JS (no jQuery, no build step).
- Auto-enhances any
<textarea class="rmeditor">. - Existing content is loaded as-is (HTML in / HTML out).
- Configurable toolbar per field.
- Paste cleaning (strips Word/Office junk) + basic XSS sanitize.
- Works with Django forms, the admin, or hand-written templates.
Install
pip install django-rmeditor
# settings.py
INSTALLED_APPS = [
...,
"rmeditor",
]
Make sure django.contrib.staticfiles is set up (it is by default) and run
collectstatic in production.
Usage
1. Hand-written templates (no form/widget)
{% load rmeditor %}
{% rmeditor_media %} {# include once per page, e.g. in your base template #}
<form method="post">
{% csrf_token %}
<textarea name="body" class="rmeditor"
data-tools="format | bold italic underline | bullist numlist | link image | removeformat">
{{ object.body }}
</textarea>
<button type="submit">Save</button>
</form>
The textarea stays in the form (hidden) and always holds the current HTML, so your
existing view code that reads request.POST["body"] keeps working unchanged.
Autochange — enhance every textarea (no class needed)
To convert all textareas on a page into editors without touching each one
(like tinymce's selector:'textarea'), enable autochange:
{% load rmeditor %}
{% rmeditor_media auto="textarea" %} {# every <textarea> becomes an editor #}
or from JS:
<script src="/static/rmeditor/js/rmeditor.js"></script>
<script>RMEditor.auto("textarea");</script> <!-- or any CSS selector -->
or globally before the script loads:
<script>window.RMEDITOR_AUTO = "textarea.rich";</script>
Opt a textarea out of autochange with class="no-rmeditor" or data-rmeditor="off".
This makes replacing tinymce a one-line change: swap the tinymce script/init for
{% rmeditor_media auto="textarea" %}.
2. Model field
from django.db import models
from rmeditor.fields import RichTextField
class Article(models.Model):
body = RichTextField(blank=True, default="")
RichTextField is a TextField subclass — same column, no data difference. Switching
an existing TextField/HTMLField to it needs only a no-op migration.
3. Form widget
from django import forms
from rmeditor.widgets import RichTextWidget
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ["body"]
widgets = {"body": RichTextWidget(tools="bold italic link")}
Toolbar tokens
format (paragraph/H1/H2/H3 dropdown), bold, italic, underline, strike,
forecolor, bullist, numlist, indent, outdent, alignleft, aligncenter,
alignright, justify, link, unlink, image, removeformat, undo, redo.
Use | for a separator. Set per field with data-tools="..." or on the widget with
RichTextWidget(tools="...").
JavaScript API
RMEditor.get(el) // instance for a textarea node or its id ("" if none)
RMEditor.getHTML(el) // current HTML
RMEditor.setHTML(el, html) // replace content
RMEditor.getText(el) // plain text, trimmed
RMEditor.syncAll() // flush every editor into its textarea
el may be the textarea DOM node or its id string.
Migrating from tinymce
- Replace the tinymce script/
tinymce.init(...)with{% rmeditor_media %}and addclass="rmeditor"to the textareas. tinymce.get("x").setContent(html)→RMEditor.setHTML("x", html)tinymce.get("x").getContent({format:"text"})→RMEditor.getText("x")
Storage stays HTML, so rendered pages ({{ field|safe }}), PDFs, and API/mobile
consumers are unaffected.
Notes / limits (v0.1)
- Uses
document.execCommand(deprecated but supported in all current browsers). - Advanced table/media editing is out of scope; existing tables/images in content are preserved and still render.
- Client-side sanitize is a basic guard. For untrusted authors, also sanitize on the
server (e.g.
bleach) before rendering with|safe.
License
MIT
Release files for django-rmeditor 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_rmeditor-0.1.0.tar.gz | 13.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_rmeditor-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.4 kB
Release files / django_rmeditor-0.1.0.tar.gz
| Download URL | django_rmeditor-0.1.0.tar.gz |
|---|---|
| Size | 13.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a36190e2e45c71a9b61ce49ffbcf71c27ed02826b13dbfa2b0050c940f6bf613
|
|
BLAKE2b-256 checksum How to use checksums |
92035d6060d08f184ac8ed60bcb30a9886fd4ee796441f19f6ec9389964ff3ce
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.1
|
Release files / django_rmeditor-0.1.0-py3-none-any.whl
| Download URL | django_rmeditor-0.1.0-py3-none-any.whl |
|---|---|
| Size | 14.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
767bd5e246d72681ca20a88e2a8c8557f432e64e5a4033c80223b4743b0106b8
|
|
BLAKE2b-256 checksum How to use checksums |
c3696d31dd586dd26f2eddcde52497cecb0272b985eff5c65d864305c546d31a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.1
|