A GitHub-style markdown editor widget for Django forms
Project description
django-markdown-widget
A GitHub-style markdown editor widget for Django forms and admin.
Documentation | PyPI | GitHub
Screenshots
| Dark (default) | Light |
|---|---|
| Split View | Preview |
|---|---|
Features
- Three view modes -- Write, Split (side-by-side live preview), Preview
- Syntax highlighting for code blocks via highlight.js (GitHub themes)
- Rich toolbar -- text formatting, headings, lists, code, tables, media, embeds
- Image, video, and document uploads with drag & drop support
- Embed support -- paste YouTube, Vimeo, CodePen URLs or raw
<iframe>code - Autosave -- drafts saved to browser localStorage, survives page reloads
- Draggable split divider -- resize editor/preview panels
- Temp upload system -- files upload to temp storage, finalize on form submit
- Light, dark, and auto theme support
- Django admin integration via one-line mixin
- Template tag and filter for server-side markdown rendering
- Pluggable renderer and upload handler architecture
- Media cleanup -- automatic orphaned file deletion + management command
- Custom undo/redo history stack (100 states)
- HTML sanitization on both client and server side
- Keyboard shortcuts -- Ctrl+B, Ctrl+I, Ctrl+K, Ctrl+Z, etc.
- Zero hard dependencies beyond Django
Installation
pip install django-markdown-widget
Add to your INSTALLED_APPS and include the URLs:
# settings.py
INSTALLED_APPS = [
...
"django_markdown_widget",
]
# urls.py
from django.urls import include, path
urlpatterns = [
...
path("md-editor/", include("django_markdown_widget.urls")),
]
For server-side rendering (template tag/filter), install a markdown library:
pip install markdown
Quick Start
Form Widget
from django.forms import ModelForm
from django_markdown_widget import MarkdownEditorWidget
class PostForm(ModelForm):
class Meta:
model = Post
fields = ["title", "content"]
widgets = {
"content": MarkdownEditorWidget(),
}
Django Admin
from django.contrib import admin
from django_markdown_widget import MarkdownEditorAdminMixin
@admin.register(Post)
class PostAdmin(MarkdownEditorAdminMixin, admin.ModelAdmin):
list_display = ["title", "created_at"]
markdown_fields = ["content"] # omit to apply to all TextFields
Template Rendering
{% load markdown_widget %}
{# As a filter (recommended) #}
{{ post.content|markdown }}
{# As a tag #}
{% markdown post.content %}
Media Cleanup
from django_markdown_widget import MarkdownCleanupMixin
class Post(MarkdownCleanupMixin, models.Model):
content = models.TextField()
# markdown_cleanup_fields = ["content"] # optional: limit to specific fields
Enable in settings:
MD_EDITOR = {
"CLEANUP_MEDIA": True,
}
Bulk cleanup via management command:
python manage.py cleanup_markdown_media --dry-run
python manage.py cleanup_markdown_media
Toolbar
Default toolbar layout, grouped by purpose:
| Group | Buttons |
|---|---|
| Text formatting | Bold, Italic, Strikethrough, Highlight |
| Structure | Heading, Quote, Horizontal rule |
| Code | Inline code, Code block |
| Lists | Bullet, Numbered, Task list |
| Links & media | Link, Image, Video, Document |
| Rich blocks | Table, Collapsible section, Embed |
| Extras | Superscript, Subscript |
| Actions | Undo, Redo |
Fullscreen toggle and autosave switch are in the top-right tabs bar.
Configuration
All settings are optional and go under MD_EDITOR in your Django settings:
MD_EDITOR = {
# Pluggable backend classes
"RENDERER_CLASS": "django_markdown_widget.renderers.DefaultRenderer",
"UPLOAD_HANDLER_CLASS": "django_markdown_widget.uploads.DefaultUploadHandler",
# Toolbar buttons (grouped by purpose)
"TOOLBAR": [
"bold", "italic", "strikethrough", "highlight", "separator",
"heading", "quote", "horizontal-rule", "separator",
"code", "code-block", "separator",
"unordered-list", "ordered-list", "task-list", "separator",
"link", "image", "video", "document", "separator",
"table", "details", "embed", "separator",
"superscript", "subscript", "separator",
"undo", "redo",
],
# Upload settings
"ALLOWED_UPLOAD_TYPES": [
"image/png", "image/jpeg", "image/gif", "image/webp",
"video/mp4", "video/webm", "video/ogg",
"application/pdf", "application/zip", "text/plain", "text/csv",
"application/json",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
],
"MAX_UPLOAD_SIZE": 50 * 1024 * 1024, # 50 MB
"UPLOAD_PATH": "md-editor/uploads/%Y/%m/",
"TEMP_UPLOAD_PATH": "md-editor/tmp/",
"TEMP_MAX_AGE": 86400, # 24 hours
# Editor defaults
"DEFAULT_HEIGHT": "300px",
"PLACEHOLDER": "Add your comment here...",
"THEME": "auto", # "light", "dark", or "auto"
# Security
"REQUIRE_AUTH": True,
# Media cleanup
"CLEANUP_MEDIA": False,
}
Custom Renderer
from django_markdown_widget import BaseRenderer
class MyRenderer(BaseRenderer):
def render(self, markdown_text: str) -> str:
import markdown_it
md = markdown_it.MarkdownIt()
return md.render(markdown_text)
MD_EDITOR = {
"RENDERER_CLASS": "myapp.renderers.MyRenderer",
}
Custom Upload Handler
from django_markdown_widget import BaseUploadHandler
class S3UploadHandler(BaseUploadHandler):
def validate(self, file):
# your validation logic
pass
def save(self, file) -> str:
# save to S3 and return the URL
return url
MD_EDITOR = {
"UPLOAD_HANDLER_CLASS": "myapp.uploads.S3UploadHandler",
}
Development
git clone https://github.com/ganiyevuz/django-md-editor.git
cd django-md-editor
uv sync --group dev
make test
make lint
Run the example app:
cd example
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver
License
MIT License. See LICENSE for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_markdown_widget-2.0.0.tar.gz.
File metadata
- Download URL: django_markdown_widget-2.0.0.tar.gz
- Upload date:
- Size: 1.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d38aab4fcfb2e402afd0e03e39b602a80d921792874c5fbe988c74ebf55852ab
|
|
| MD5 |
6c7dff9498af807f21508ad9a871a82f
|
|
| BLAKE2b-256 |
79769b7e95e9382670b37ba25b172b9c1dbcb58abfbaf918839bb8255cdbe1e6
|
File details
Details for the file django_markdown_widget-2.0.0-py3-none-any.whl.
File metadata
- Download URL: django_markdown_widget-2.0.0-py3-none-any.whl
- Upload date:
- Size: 126.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b51065ba3cbfeab416b9728c9fd964cd83346b1db2769c4fa524d51753e2e004
|
|
| MD5 |
9401f2926d75e01cf01a62ceea37705c
|
|
| BLAKE2b-256 |
acb9f545fdf733697c7918d42f68c4c85f9cd08a66a5fa74248deaa8a39f3c43
|