A JavaScript rich text editor with no build step.
No npm install, no bundler, no framework, no transpiler. Two script tags, one stylesheet, and a single new RichTextEditor()call gives you a full WYSIWYG editor — tables, images, AI, track changes and clean HTML out. It is plain ES5-compatible JavaScript, so it runs anywhere a <script> tag runs.
The whole editor is static files.
Three tags and a constructor
That is a working editor with the default toolbar. Open it in a browser and start typing — there is no build step between those lines and a running editor.
<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css" />
<script src="/richtexteditor/rte.js"></script>
<script src="/richtexteditor/plugins/all_plugins.js"></script>
<div id="div_editor1">Hello RichTextEditor</div>
<script>
var editor = new RichTextEditor("#div_editor1");
</script>Configure it with a plain object
Every option is a property on the second argument. Presets cover most cases, and each one is just a named toolbar definition.
var editor = new RichTextEditor("#div_editor1", {
toolbar: "basic", // "basic" | "full" | "mobile" | "custom"
height: "500px",
showStatistics: true, // word / character count in the status bar
showFloatTextToolBar: true // selection popup toolbar
});Build your own toolbar
Name your layout and the editor picks it up. The grouping syntax controls the separators, so the result is a toolbar you own rather than one you fight.
var editor = new RichTextEditor("#div_editor1", {
toolbar: "custom",
// The editor resolves the toolbar by name: toolbar: "custom" makes it read
// toolbar_custom. The built-ins (toolbar_full, toolbar_basic, toolbar_mobile)
// follow exactly the same convention, so your layout is a peer of theirs.
toolbar_custom:
"{bold,italic,underline}|" +
"{insertorderedlist,insertunorderedlist}|" +
"{insertlink,insertimage}|" +
"{aiassist,aichat}|" +
"{undo,redo}"
});Getting content in and out
The API is ordinary method calls on the instance — no virtual DOM, no reconciliation, no lifecycle to respect. Store what comes out like any other user-submitted HTML, and sanitize server-side before rendering it back to other users: see why “nothing executes in the editor” is the wrong bar.
// Read what the user wrote — publish-ready HTML.
var html = editor.getHTMLCode();
// Put content back in.
editor.setHTMLCode("<p>Loaded from your API</p>");
// React to edits (mirror into a hidden field, autosave, enable a Save button...)
editor.attachEvent("change", function () {
document.getElementById("inp_htmlcode").value = editor.getHTMLCode();
});Uploads go to your own endpoint
Set one global and every inserted image or file posts wherever you decide. Nothing is uploaded to us, which is what makes the editor viable where data residency is a requirement.
// Called for every file the user inserts. Post it wherever you like and
// hand back the stored URL — the editor never talks to a vendor service.
window.rte_file_upload_handler = function (file, callback) {
var form = new FormData();
form.append("file", file);
var xhr = new XMLHttpRequest();
xhr.open("POST", "/your-upload-endpoint");
xhr.onload = function () { callback(xhr.responseText); };
xhr.send(form);
};Why plain JavaScript is still the right choice
- It outlives framework churn: an editor wired to a script tag does not need rewriting when your front end changes. The same integration has survived several framework generations.
- Everything is included: AI Toolkit (bring your own key), tracked changes, comments, tables, and Word/PDF export ship in the base license — no premium add-on tiers.
- Perpetual license from $129: one purchase, no per-load metering — see pricing and the TinyMCE / CKEditor comparisons.
Using a framework? There are dedicated guides for React, Vue, Angular, Svelte, Next.js, Blazor, ASP.NET Core, and PHP.