Chuyển đến nội dung chính

The Native-First, High-Performance Markdown Plugin for JavaScript

Markdown text editor plugin that transforms a native textarea into a full editing suite. WYSIWYG and plain Markdown modes, live preview, RTL support, dark mode. Works with Django, HTMX, Laravel, React, and any stack.

The Philosophy: "Native-First"

Most editors break the standard web workflow. MarkdownEditor embraces it. Because it sits directly on top of a <textarea>, you don't need to learn a new way to handle data.

  • No Data Binding Needed: Works with <form method="POST"> out of the box
  • Standard Access: Use document.getElementById('editor').value just like a normal input.
  • Backend Agnostic: Works with any backend (Python, Node.js, PHP, etc.) just like a normal form field

Key Features

🖼️ Advanced Image Upload (SEO Optimized)

Don't bloat your database with heavy Base64 strings. Configure our API to upload images directly to your server or S3 bucket. The editor receives the URL, keeping your Markdown files light and your site's SEO ranking high.

🔀 Hybrid & Plain Modes

Give your users the best of both worlds. Switch between a Visual (WYSIWYG) Hybrid mode for easy formatting and a Plain Markdown mode for distraction-free, raw syntax editing.

🌍 Global-Ready with RTL Support

Full native support for Right-to-Left (RTL) languages. Perfect for projects requiring Arabic, Urdu, or Farsi support with automatic text-direction alignment.

⚡ Performance at Scale

  • Lightweight: A tiny ~116KB footprint that won't slow down your page load
  • Large Document Support: Optimized to handle thousands of lines of text without input lag or browser freezing
  • Smart Rendering: Debounced preview updates, cached style calculations, and conflict-free keyboard handling between list continuation and indentation — so Tab and Enter always do exactly one thing

♿ Accessible by Default

Full ARIA support is built in and requires no extra configuration. The toolbar is a proper role="toolbar" landmark, the preview pane is a labelled role="region", all SVG icons are hidden from screen readers, the preview toggle exposes its on/off state via aria-pressed, and disabled toolbar buttons use both disabled and aria-disabled so assistive technology is never misled. Modals return focus to the triggering button when closed.

🛡️ Zero CSS Conflicts

All editor styles are fully scoped to the .markdown-editor-wrapper element. Tailwind's global preflight (element resets for h1h6, a, button, etc.) is excluded, so the editor can live alongside Bootstrap, Tailwind, or any other CSS framework on the same page without breaking a single style.

🔒 XSS Safe Preview

The rendered preview is sanitized via DOMPurify before being written to the DOM. Script tags, inline event handlers, and malicious URLs in crafted markdown input are stripped automatically — no configuration required.

Markdown Editor Demo

Quick Implementation

1. Installation

You can install it using NPM, import the JavaScript file directly, or use a CDN for rapid deployment.

Install via NPM

bash
npm install markdown-text-editor
OR

Using a CDN

Alternatively, include the following CDN links in your HTML

html
<script src="https://cdn.jsdelivr.net/npm/markdown-text-editor"></script>

2. The HTML Structure

Simply place a <textarea> inside your standard form. No special wrappers required.

html
1
2
3
4
<form action="/api/save" method="POST">
  <textarea id="markdown-editor" name="content"># Hello World</textarea>
  <button type="submit">Save Content</button>
</form>

3. Import JS and CSS and initialise the plugin on your textarea element

javascript
1
2
3
4
5
6
import MarkdownEditor from "markdown-text-editor";

const editor = new MarkdownEditor('#markdown-editor', {
  placeholder: 'Write your markdown...',
  toolbar: ['heading', 'bold', 'italic', 'strikethrough', 'ul', 'ol', 'checklist', 'blockquote', 'link', 'preview'],
});

4. Configuration & Customization

You can fully customize the editor's behavior and interface by passing an options object. If you omit an option, the default value is used.

Property Kiểu Mặc định Mục đích
mode string 'plain' Sets the initial view. Use 'hybrid' for a WYSIWYG experience or 'plain' for raw syntax.
placeholder string 'Write...' Text shown when the editor is empty.
toolbar array [...] Defines which tools appear and in what order.
footer false | object all visible Controls the status bar shown below the editor. Set to false to hide it entirely, or pass an object to toggle individual stats.
theme string inherited Explicitly sets the editor theme ('light', 'dark', 'snowberry', 'darkberry'). If omitted, the editor inherits data-theme from the nearest ancestor element or the <textarea> itself.
minHeight number 200 Minimum height in pixels the editor will shrink to when content is short. Pairs with maxHeight to set the auto-grow range.
maxHeight number 500 Maximum height in pixels the editor can grow to in non-fullscreen mode. Once content exceeds this height a scrollbar appears inside the editor. The editor also has a drag handle so users can manually resize it beyond this limit.
onChange function undefined Callback fired on every content change — typing, toolbar actions, undo/redo, and list continuation. Receives the current markdown string as its only argument.

🛠 Toolbar Customization

The toolbar is modular. You can create a minimal experience or a full-featured power suite by modifying the array.

Available Tools
Category Tool Keys
Typography heading, bold, italic, strikethrough, blockquote
Lists ul (bullet), ol (numbered), checklist
Code code (inline), codeblock (fenced block)
Inserts hr (horizontal rule), table (table template)
Media link, image
Editing undo, redo, indent, outdent
View preview
💡 Implementation Tips:
  • Reordering: The buttons appear in the exact order you list them in the array
  • Removing: Simply omit any key (like image) from the array to disable that feature entirely for the user
  • Native Fallback: If you don't provide a placeholder in JS, the plugin will automatically use the placeholder attribute from your HTML <textarea>

📊 Footer (Status Bar)

The footer sits below the editor and shows the cursor's line, column, the document's character count, and optionally the word count — all updated in real time. It is visible by default and each stat can be toggled independently.

Key Kiểu Mặc định Sự miêu tả
line boolean true Show the current line number.
col boolean true Show the current column number.
chars boolean true Show the total character count.
words boolean false Show the total word count. Off by default — set to true to enable.
Usage Examples
javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Default — line, col, and chars visible
new MarkdownEditor('#editor');

// Disable the footer entirely
new MarkdownEditor('#editor', { footer: false });

// Hide only character count
new MarkdownEditor('#editor', { footer: { chars: false } });

// Hide line and column, keep character count
new MarkdownEditor('#editor', { footer: { line: false, col: false } });

// Show only line number
new MarkdownEditor('#editor', { footer: { col: false, chars: false } });

// Enable word count alongside the defaults
new MarkdownEditor('#editor', { footer: { words: true } });

// Show word count only
new MarkdownEditor('#editor', { footer: { line: false, col: false, chars: false, words: true } });

5. Getting, Setting, and Submitting Content

One of the core strengths of MarkdownEditor is that it keeps the underlying <textarea> perfectly synchronized. Whether you are using a modern JavaScript framework or a traditional backend like Django, PHP, or Laravel, the workflow remains simple and native.

1. The Native Way (Recommended)

Because the editor enhances a standard textarea, you can use familiar DOM methods. This is the fastest way to interact with your data without learning a new API.

javascript
1
2
3
4
5
// Retrieve content via ID
const markdown = document.getElementById('markdown-editor').value;

// Set content via ID (The editor UI updates automatically)
document.getElementById('markdown-editor').value = "# New Heading Content";

2. Using a Variable Reference

If you have a reference to the textarea element, you can use it directly — no library-specific API needed.

javascript
1
2
3
4
5
6
7
const textarea = document.getElementById('markdown-editor');

// Retrieve content
const markdown = textarea.value;

// Set content (the editor UI reflects this immediately)
textarea.value = "## Updated via JS";

3. Setting and reading content

The recommended way to set initial content is directly in the <textarea> HTML — this works naturally with every backend framework (Django, Laravel, Rails, PHP, etc.) and the editor renders it automatically on init.

html
1
2
<!-- Recommended: set content server-side -->
<textarea id="markdown-editor"># Hello World</textarea>

To read or update content at runtime, use the native textarea value. Call editor.render() after an update to refresh the preview and hybrid layer.

javascript
1
2
3
4
5
6
7
8
const textarea = document.getElementById('markdown-editor');

// Read
const markdown = textarea.value;

// Update at runtime
textarea.value = '# New content';
editor.render();

4. Tearing down the editor — destroy()

Call editor.destroy() to remove the editor DOM wrapper and restore the original <textarea> to its position in the document. Useful in single-page applications when unmounting a view.

javascript
1
2
3
4
const editor = new MarkdownEditor('#markdown-editor');

// Remove the editor and restore the plain textarea
editor.destroy();
Reacting to changes with onChange

Pass an onChange callback to be notified on every content change. Receives the current markdown string.

javascript
1
2
3
4
5
const editor = new MarkdownEditor('#markdown-editor', {
    onChange(value) {
        console.log('Content changed:', value.length, 'characters');
    }
});
Draft auto-save with localStorage

Use onChange to save a draft on every keystroke. Restore it by pre-filling the textarea before initialising the editor.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const DRAFT_KEY = 'my-page-draft';

// Restore saved draft before init (only if textarea starts empty)
const textarea = document.getElementById('markdown-editor');
const saved = localStorage.getItem(DRAFT_KEY);
if (saved && !textarea.value) textarea.value = saved;

// Save on every change
const editor = new MarkdownEditor('#markdown-editor', {
    onChange(value) {
        localStorage.setItem(DRAFT_KEY, value);
    }
});

// Clear draft after successful form submission
document.querySelector('form').addEventListener('submit', () => {
    localStorage.removeItem(DRAFT_KEY);
});

4. Effortless Automatic Form Submission

Because MarkdownEditor is built directly on the native <textarea>, it is compatible with every backend framework (Django, Laravel, PHP, Ruby on Rails, etc.) right out of the box.

This is where the "Native-First" philosophy shines. You don't need to manually sync data before submitting a form. The browser treats the editor exactly like a standard input field.

html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<form method="POST" action="/api/submit">
  <textarea id="markdown-editor" name="content" class="h-48" rows="5">
    # Initial Content
  </textarea>

  <button type="submit">Submit to Server</button>
</form>

<script>
  // Just initialize it. That's it.
  new MarkdownEditor('#markdown-editor');
</script>

Note: MarkdownEditor plugin initialization mandatory

Just use a standard HTML <form>. The name attribute on the textarea is what your server will use to identify the content.

🚀 Why it's a Game-Changer for Backends

Since the editor preserves the native <textarea> behavior, your server handles the data as a standard string. There is zero extra logic required—no preventDefault() and no manual FormData construction.

💡 Why this is a "Killer Feature":

Most editors (like Quill, Editor.js, simpleMDE, easyMDE) save data in complex JSON structures. If a developer uses those, they have to rewrite their database schema and their rendering logic.

With MarkdownEditor, a developer can take an old website, replace a plain <textarea> with your editor, and the backend doesn't even know it changed. It just receives the same raw text it always did, but the user gets a 10x better experience.

Framework / Language How to access the Markdown content
PHP $_POST['content']
Django request.POST.get('content')
Node.js (Express) req.body.content
Laravel $request->input('content')
Ruby on Rails params[:content]

Configuration Options

🖼️ Advanced image upload

Handling image uploads natively—rather than relying on slow, memory-heavy Base64 strings—is a significant win for both performance and SEO.

Configuration Options

The image tool supports a fileInput configuration to handle direct server uploads.

  • accept: Define an array of allowed image formats (e.g., 'webp', 'avif')
  • uploadUrl: Specify the backend endpoint where the File object will be sent via POST
  • params: Optional object to send additional data (like CSRF tokens, user IDs, or folder names) alongside the image file

Usage example (Full Config)

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
const options = {
  placeholder: 'Start writing...',
  toolbar: [
    'link',
    {
      image: {
        fileInput: {
            accept: ['webp', 'avif'], // restrict the image upload format
            uploadUrl: '/api/upload', // Your upload endpoint
            params: {
                _token: 'your_csrf_token_here', // Essential for Laravel/Django
                folder: 'blog_posts'
            }
        },
        // Supports boolean: true/false OR object: { required: true }
        altInput: { required: true }
      }
    },
    'preview'
  ],
}
const editor = new MarkdownEditor('#markdown-editor', options);

📡 Server Integration Details

1. The Request

The editor sends a POST request as multipart/form-data. By default, it includes:

  • image_file: The actual file object
  • image_alt: The alt text entered by the user
  • ...plus any custom data defined in the params object
2. The Required Response

To confirm a successful upload and insert the image into the editor, your server must return the following JSON structure:

json
1
2
3
4
{
  "success": true,
  "image_path": "https://cdn.yourdomain.com/uploads/image.webp"
}

Note: Ensure you use the key image_path for the URL of the uploaded image.

Xác thực văn bản thay thế hình ảnh (altInput)

Để đảm bảo nội dung của bạn vẫn có thể truy cập được và thân thiện với SEO, MarkdownEditor thực thi xác thực văn bản thay thế theo mặc định. Bạn có thể định cấu hình hành vi này bằng cách sử dụng tốc ký boolean hoặc đối tượng chi tiết.

  • Hành vi mặc định: Nếu altInput không được xác định, nó sẽ mặc định là { bắt buộc: true
  • Thực thi khả năng truy cập: Người dùng sẽ bị ngăn chèn hình ảnh cho đến khi mô tả thay thế được cung cấp
Ví dụ về cấu hình:
1. Mặc định (Không cần cấu hình)
javascript
1
2
3
4
// Alt text is REQUIRED by default
image: {
  fileInput: { uploadUrl: '/api/upload' }
}
2. Viết tắt (Tắt xác thực)

Nếu bạn muốn cho phép hình ảnh không có mô tả, chỉ cần đặt boolean thành false.

javascript
1
2
3
image: {
  altInput: false // Users can now skip the alt text field
}
3. Dựa trên đối tượng (Rõ ràng)
javascript
1
2
3
4
5
image: {
    altInput: {
        required: false // Disables alt text validation — users can skip the alt field
    }
}

Cách sử dụng hình ảnh tiêu chuẩn (Không có fileInput):

Nếu fileInput không được định cấu hình, trình chỉnh sửa sẽ mặc định sử dụng phương thức dựa trên URL đơn giản. Điều này lý tưởng nếu người dùng của bạn chủ yếu liên kết đến các máy chủ hình ảnh bên ngoài.

javascript
1
2
3
4
5
6
7
8
const options = {
  toolbar: [
    'link',
    'image',
    'preview'
  ],
}
const editor = new MarkdownEditor('#markdown-editor', options);
💡 Tại sao nên sử dụng thông số?

Trong các framework như Laravel hoặc Django, bạn không thể tải tệp lên mà không có mã thông báo CSRF. Bằng cách thêm _token vào đối tượng params, yêu cầu của bạn sẽ chuyển qua phần mềm trung gian bảo mật của chương trình phụ trợ một cách liền mạch, duy trì triết lý "Zero Logic" cho bộ điều khiển phía máy chủ của bạn.

🔀 Chế độ chỉnh sửa

MarkdownEditor cung cấp hai cách riêng biệt để viết và định dạng nội dung của bạn. Bạn có thể chuyển đổi giữa chế độ xem tập trung vào cú pháp truyền thống hoặc trải nghiệm hiện đại, ưu tiên hình ảnh.

Cấu hình

Bạn có thể xác định chế độ bắt đầu bằng thuộc tính mode trong quá trình khởi tạo.

  • plain (Mặc định): Môi trường Markdown rõ ràng, hiệu suất cao trong đó hiển thị cú pháp (như **bold** hoặc # tiêu đề). Lý tưởng cho các nhà phát triển và những người theo chủ nghĩa thuần túy Markdown
  • kết hợp: Trải nghiệm lấy cảm hứng từ WYSIWYG giúp hiển thị định dạng (Đậm, Nghiêng, Tiêu đề) trong thời gian thực khi bạn nhập, trong khi vẫn duy trì cấu trúc Markdown cơ bản.
Thực hiện:
javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Default initialization (Plain Mode)
new MarkdownEditor('#markdown-editor');

// Explicit Plain Mode
new MarkdownEditor('#markdown-editor', {
    mode: 'plain'
});

// Hybrid (Visual) Mode
new MarkdownEditor('#markdown-editor', {
    mode: 'hybrid'
});
Xem trước chế độ kết hợp và đơn giản:
Chế độ kết hợp

Định dạng trực quan được hiển thị theo thời gian thực trong khi bạn nhập.

Chế độ đơn giản (Mặc định)

Tập trung vào cú pháp Markdown thô để có trải nghiệm nhẹ nhàng.

Cấu hình Thuộc tính/Công cụ Sự miêu tả
Đối tượng tùy chọn placeholder Đặt văn bản giữ chỗ cho vùng văn bản (tùy chọn, vì bạn cũng có thể sử dụng thuộc tính vùng văn bản HTML tiêu chuẩn)
mode: 'hybrid' Cho phép trải nghiệm lấy cảm hứng từ WYSIWYG hiển thị định dạng (in đậm, in nghiêng, tiêu đề) trong thời gian thực khi bạn nhập
thanh công cụ: Xác định công cụ nào
xuất hiện trên thanh công cụ và thứ tự của chúng.
heading Mở menu thả xuống để chọn cấp tiêu đề H1–H6
bold Cho phép định dạng văn bản in đậm.
italic Cho phép định dạng văn bản in nghiêng.
strikethrough Cho phép gạch ngang văn bản.
ol (Danh sách có thứ tự): Chuyển văn bản sang dạng danh sách đánh số.
ul (Danh sách không có thứ tự): Chuyển văn bản thành danh sách dấu đầu dòng.
checklist Thêm các hộp kiểm vào văn bản của bạn, giúp nó trở nên tuyệt vời cho các nhiệm vụ, danh sách việc cần làm hoặc theo dõi trạng thái hoàn thành.
blockquote Đánh dấu văn bản được trích dẫn hoặc nhấn mạnh.
code Bao văn bản đã chọn bằng dấu backticks đơn cho mã nội tuyến. Nhấp lại sẽ loại bỏ dấu tích ngược.
codeblock Bao bọc văn bản đã chọn trong khối mã có hàng rào ba dấu ngược. Nhấp chuột một lần nữa sẽ loại bỏ hàng rào.
hr Chèn một quy tắc ngang --- vào vị trí con trỏ trên dòng riêng của nó.
table Chèn mẫu bảng đánh dấu 2x3 ban đầu vào vị trí con trỏ.
image Cho phép bạn chèn hình ảnh thông qua cú pháp đánh dấu.
link Cho phép bạn thêm siêu liên kết vào văn bản của mình.
undo Để đảo ngược những thay đổi cuối cùng.
redo Để áp dụng lại những thay đổi được hoàn tác gần đây nhất.
indent Để tăng mức độ thụt lề.
outdent Để giảm mức độ thụt lề.
preview Chuyển đổi chế độ xem trước toàn màn hình cạnh nhau. Các hộp kiểm trong khung xem trước có thể nhấp vào được và cập nhật nguồn đánh dấu ngay lập tức. Nhấn Escape để thoát toàn màn hình.
Tính năng tải hình ảnh nâng cao lên:
Cho phép định cấu hình tải hình ảnh lên
máy chủ của riêng bạn và đặt đường dẫn hình ảnh
thông qua API. Điều này cải thiện hiệu suất và SEO.
fileInput chấp nhận: Mảng loại tệp hình ảnh được phép (ví dụ: 'webp', 'avif').
uploadUrl: Điểm cuối phụ trợ nơi tệp được gửi qua POST.
params: Đối tượng tùy chọn cho dữ liệu bổ sung (mã thông báo CSRF, ID người dùng, tên thư mục)
altInput bắt buộc: false: Tắt xác thực nhập văn bản thay thế (mặc định là đúng)

⌨️ Phím tắt

Các hành động định dạng phổ biến có thể được kích hoạt trực tiếp từ bàn phím mà không cần chạm vào thanh công cụ. Mỗi phím tắt cũng được hiển thị trong chú giải công cụ của nút thanh công cụ tương ứng.

Phím tắt Hoạt động
Ctrl + B  /  ⌘ B Chuyển đổi Đậm
Ctrl + I  /  ⌘ I Chuyển đổi nghiêng
Ctrl + K  /  ⌘ K Chèn liên kết
Ctrl + `  /  ⌘ ` Chuyển đổi nội tuyến
Ctrl + Shift + S  /  ⌘ ⇧ S Chuyển đổi Gạch ngang
Ctrl + Z  /  ⌘ Z Hoàn tác
Ctrl + Shift + Z  /  ⌘ ⇧ Z Làm lại
Tab Thụt lề các dòng đã chọn
Shift + Tab Nhô ra các dòng đã chọn
Ctrl + F  /  ⌘ F Mở bảng Tìm kiếm
Ctrl + H  /  ⌘ H Mở bảng Tìm & Thay thế
Escape Đóng Tìm bảng điều khiển/Thoát xem trước toàn màn hình

🔍 Tìm & amp; Thay thế

Bảng điều khiển tìm và thay thế tích hợp có sẵn bên trong trình chỉnh sửa — không cần tiện ích mở rộng trình duyệt hoặc công cụ riêng biệt.

  • Nhấn Ctrl + F (hoặc ⌘ F) để mở bảng Tìm
  • Nhấn Ctrl + H (hoặc ⌘ H) để mở Tìm & Thay thế bảng điều khiển
  • Tìm kiếm không phân biệt chữ hoa chữ thường và hiển thị bộ đếm kết quả khớp trực tiếp (ví dụ: 3 trên 12)
  • Điều hướng các kết quả khớp bằng các nút ▲ / ▼ hoặc Enter / Shift + Enter
  • Thay thế thay thế kết quả được đánh dấu hiện tại; Thay thế tất cả thay thế mọi lần xuất hiện cùng một lúc
  • Nhấn Escape để đóng bảng và trả tiêu điểm về trình chỉnh sửa

Bảng điều khiển nổi ở góc trên bên phải của khu vực nội dung trình soạn thảo và không làm gián đoạn quá trình viết.

🌙 Chủ đề

MarkdownEditor tự động kế thừa chủ đề của nó từ trang xung quanh — không cần cấu hình. Trình chỉnh sửa đọc data-theme từ tổ tiên gần nhất khi khởi tạo, để nó luôn đồng bộ với chủ đề trang web của bạn ngay từ đầu.

Cách giải quyết chủ đề (thứ tự ưu tiên)

  1. tùy chọn chủ đề — ghi đè rõ ràng được truyền trong đối tượng tùy chọn
  2. data-theme trên <textarea> — được đặt trực tiếp trên phần tử
  3. data-theme trên bất kỳ tổ tiên nào — ví dụ: <html>, <body> hoặc trình bao bọc <div>

Chủ đề có sẵn

'light' (mặc định), 'dark', 'snowberry', 'darkberry'

Tùy chọn 1 — kế thừa từ <html> hoặc bất kỳ tổ tiên nào (cấu hình không)
html
1
2
3
4
5
6
<html data-theme="dark">
  ...
  <textarea id="markdown-editor"></textarea>
  <script>
    new MarkdownEditor('#markdown-editor'); // picks up dark automatically
  </script>
Tùy chọn 2 — đặt data-theme trực tiếp trên <textarea>
html
1
2
3
4
<textarea id="markdown-editor" data-theme="dark"></textarea>
<script>
  new MarkdownEditor('#markdown-editor');
</script>
Tùy chọn 3 — tùy chọn theme rõ ràng (ghi đè mọi thứ)
javascript
1
2
3
new MarkdownEditor('#markdown-editor', {
    theme: 'dark'
});

🎨 Chủ đề tùy chỉnh thông qua các biến CSS

Bạn hoàn toàn có thể tùy chỉnh giao diện của trình chỉnh sửa bằng cách ghi đè các biến CSS của nó trên phần tử .markdown-editor-wrapper hoặc bất kỳ bộ chọn [data-theme] nào. Tất cả các màu đều sử dụng không gian màu OKLCH để mang lại kết quả đồng nhất về mặt nhận thức.

Biến Mục đích Ánh sáng mặc định Mặc định tối
--color-base Nền soạn thảo oklch(100% 0 0) oklch(10.9% 0 0)
--color-on-base Màu văn bản chính oklch(22% 0 0) oklch(98% 0 0)
--color-primary Giọng chính (thanh công cụ đang hoạt động, liên kết) oklch(51.1% .262 277) oklch(66.4% .184 286)
--color-on-primary Văn bản trên bề mặt màu cơ bản oklch(96.2% .018 272) oklch(10% .01 270)
--color-secondary Giọng phụ oklch(59.1% .293 323) oklch(65% .18 220)
--color-accent Đánh dấu trọng âm (mã nội tuyến, in nghiêng) oklch(54.1% .281 293) oklch(75% .18 50)
--color-neutral Bề mặt trung tính (viền, vách ngăn) oklch(15% 0 0) oklch(85% 0 0)
--color-error Màu trạng thái lỗi oklch(57.7% .245 27) oklch(60% .22 30)
--border-radius Làm tròn góc của khung soạn thảo 0.25rem
Ví dụ về chủ đề tùy chỉnh

Ghi đè bất kỳ biến nào trên .markdown-editor-wrapper sau khi trình soạn thảo khởi tạo hoặc xác định khối [data-theme] tùy chỉnh trong biểu định kiểu của bạn:

css
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/* Override individual variables */
.markdown-editor-wrapper {
    --color-primary: oklch(60% 0.2 30);   /* orange accent */
    --border-radius: 0.5rem;
}

/* Or define a full custom theme */
[data-theme="brand"] .markdown-editor-wrapper,
.markdown-editor-wrapper[data-theme="brand"] {
    --color-base:       oklch(15% 0.01 250);
    --color-on-base:    oklch(95% 0 0);
    --color-primary:    oklch(65% 0.22 145);   /* green */
    --color-on-primary: oklch(10% 0 0);
    --color-accent:     oklch(75% 0.18 60);
    --color-neutral:    oklch(80% 0 0);
    --border-radius:    0.75rem;
}
javascript
new MarkdownEditor('#markdown-editor', { theme: 'brand' });
  • Xem trước theo thời gian thực: Xem phần đánh dấu của bạn được hiển thị ngay lập tức khi bạn nhập.
  • Đánh dấu cú pháp: Khả năng đọc nâng cao với mã rõ ràng và định dạng đánh dấu.
  • Tích hợp dễ dàng: Tích hợp liền mạch vào bất kỳ dự án web nào với thiết lập tối thiểu.
  • Thanh công cụ có thể tùy chỉnh: Tự động định cấu hình và sắp xếp lại các tùy chọn thanh công cụ như in đậm, in nghiêng, v.v.

Đặc trưng

🔌 Tích hợp biểu mẫu gốc

Hoạt động chính xác như <textarea> tiêu chuẩn. Không có API phức tạp—chỉ cần sử dụng thuộc tính value hoặc name. Nó "chỉ hoạt động" với việc gửi biểu mẫu HTML tiêu chuẩn bằng PHP, Django hoặc Node.js.

🖼️ Tải lên hình ảnh nâng cao

Định cấu hình tải lên máy chủ gốc thông qua API. Tránh các chuỗi Base64 nặng để đảm bảo tải trang nhanh hơn và SEO vượt trội bằng cách lưu trữ hình ảnh trên CDN của riêng bạn.

🔀 Hybrid & Plain Modes

Chuyển đổi giữa trải nghiệm Kết hợp (WYSIWYG) để chỉnh sửa trực quan hoặc chế độ Đánh dấu đơn giản để có cảm giác viết mã truyền thống.

🚀 Hiệu suất cao

Gói ~116KB nhỏ được tối ưu hóa cho "Nội dung nặng". Xử lý các tài liệu lớn và các tệp lớn mà không có bất kỳ độ trễ đầu vào hoặc giảm hiệu suất nào.

🌍 Hỗ trợ RTL tích hợp

Hỗ trợ gốc cho các ngôn ngữ từ phải sang trái như tiếng Ả Rập, tiếng Urdu và tiếng Farsi. Hoàn hảo để xây dựng các ứng dụng có thể truy cập toàn cầu.

🌙 Chủ đề thích ứng

Bao gồm hỗ trợ Chế độ tối tự động. Nó đồng bộ hóa với cài đặt hệ thống của bạn hoặc thư viện Giao diện người dùng Frutjam để có trải nghiệm hình ảnh liền mạch.

📝 Chỉnh sửa thông minh

Tính năng tiếp tục danh sách tự động theo kiểu GitHub dành cho danh sách có thứ tự, danh sách không có thứ tự và danh sách kiểm tra — nhấn Enter và trình chỉnh sửa sẽ tiếp tục mẫu. Các hộp kiểm trong khung xem trước có thể nhấp vào được và đồng bộ hóa trở lại nguồn đánh dấu ngay lập tức.

📱 Hoàn toàn đáp ứng

Giao diện người dùng linh hoạt, ưu tiên thiết bị di động, thích ứng hoàn hảo với máy tính để bàn, máy tính bảng và điện thoại thông minh để chỉnh sửa khi đang di chuyển.

📦 Hỗ trợ toàn cầu

Tương thích với ESM, UMD, CommonJS và IIFE. Hoạt động ngay lập tức thông qua CDN (<script src>), npm hoặc bất kỳ gói nào (Vite, webpack, Rollup) — không cần cấu hình bổ sung.

♿ Accessible by Default

Hỗ trợ ARIA đầy đủ được tích hợp sẵn — mốc thanh công cụ, vùng xem trước được gắn nhãn, các nút thân thiện với trình đọc màn hình, aria-pressed trên nút chuyển đổi xem trước, đã tắtaria-disabled trên các công cụ không hoạt động và khôi phục tiêu điểm chính xác khi các chế độ đóng.

🛡️ Zero CSS Conflicts

Các kiểu trình soạn thảo hoàn toàn nằm trong phạm vi .markdown-editor-wrapper. Tính năng kiểm tra trước toàn cầu của Tailwind bị loại trừ để trình soạn thảo hoạt động an toàn cùng với Bootstrap, Tailwind hoặc bất kỳ khung nào khác mà không phá vỡ phong cách của chúng.

⌨️ Phím tắt

Ctrl+B, Ctrl+I, Ctrl+K, Ctrl+`, Ctrl+Shift+S — các thao tác định dạng phổ biến mà không cần chạm vào chuột. Mỗi phím tắt được hiển thị trong chú giải công cụ của nút thanh công cụ.

🔍 Tìm & amp; Thay thế

Nhấn Ctrl+F để tìm hoặc Ctrl+H để mở find & thay thế. Tìm kiếm không phân biệt chữ hoa chữ thường với bộ đếm so khớp trực tiếp, điều hướng tiếp theo/trước, thay thế một lần và thay thế tất cả — mà không cần rời khỏi trình chỉnh sửa.

Ví dụ về cấu hình đầy đủ

Sử dụng ví dụ toàn diện này để khởi tạo MarkdownEditor với tất cả các tính năng chính, bao gồm sắp xếp thanh công cụ tùy chỉnh và xử lý tải lên hình ảnh nâng cao.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const editor = new MarkdownEditor('#markdown-editor', {
    mode: 'hybrid',
    placeholder: 'Start writing...',
    footer: {
        line: true,
        col: true,
        chars: true,
        words: true,
    },
    onChange(value) {
        console.log('Content updated:', value.length, 'characters');
    },
    toolbar: [
        'heading', 'bold', 'italic', 'strikethrough', 'blockquote',
        'ul', 'ol', 'checklist',
        'code', 'codeblock', 'hr', 'table',
        {
            image: {
                fileInput: {
                    accept: ['webp', 'avif', 'png'],
                    uploadUrl: '/api/upload'
                }
            }
        },
        'link', 'undo', 'redo', 'indent', 'outdent', 'preview'
    ],
});

// Read content natively
const markdown = document.getElementById('markdown-editor').value;

// Update content programmatically (dispatch input to keep preview in sync)
// const ta = document.getElementById('markdown-editor');
// ta.value = '# New content';
// ta.dispatchEvent(new Event('input', { bubbles: true }));

// destroy() when the view unmounts (SPAs)
// editor.destroy();