§ blog · Development06/29/2026
← All articles

Autumn Note: why we built our own zero-dependency, open-source rich-text editor

Most internal systems — CMS, admin portals, knowledge bases — need a formatted text editor somewhere. Instead of pulling in jQuery or a heavy commercial library, we built Autumn Note: a vanilla ES2022 WYSIWYG editor, zero dependencies, MIT-licensed and open source.

DevelopmentOpen SourceWYSIWYGTypeScript7 min read
By KonexForge Engineering Team
INPUT KHÔNG TIN CẬYPaste HTMLsetHTML()Typed inputDOM Sanitizerstrip script · on* · js:AUTUMN NOTE CORE · ES2022Toolbar + Bubble toolbarsticky · floating on selectContenteditable DOMvanilla JS · zero depsHistory (undo/redo)configurable depthMarkdown shortcuts# · > · - → HTMLgetHTML() · setHTML() · getMarkdown()MỞ RỘNGPlugin APIinstall() · uninstall()AutumnNote.use()đăng ký toàn cụcReact wrapperref.getHTML()Vue wrapperref.value.editorZERO DEPENDENCY · ES2022 · MIT LICENSE · OPEN SOURCEautumn.konexforge.com

Most internal systems we build — admin portals, CMS, knowledge bases, customer support forms — have at least one place where users need to enter formatted content: bold text, inserted images, tables, not just a plain textarea. The familiar solutions for this — Summernote, Quill, TinyMCE — all work, but each comes with a cost that's often overlooked: a jQuery dependency (Summernote), a large bundle for a component used on nearly every page, or a commercial license once advanced features are needed (TinyMCE, Froala). After patching these libraries one too many times to fit a client's exact requirements, we decided to build Autumn Note — a zero-dependency, MIT-licensed open-source rich-text editor.

Why zero-dependency matters for a component used everywhere

A rich-text editor isn't a library used on a single page — it tends to show up in a CMS, a support form, an email composer, a comment box, and several other places within the same product. Any dependency it pulls in (jQuery, a specific UI framework) multiplies with every place the component is used, or forces a project to install an entire runtime just for a text box. Autumn Note is written in pure vanilla ES2022+ JavaScript — no jQuery, no required framework — built with Vite into both an ES Module (tree-shakeable) and a UMD bundle (usable directly via a `<script>` tag without a bundler), with TypeScript definitions bundled in rather than left for consumers to declare themselves.

A feature set built for real products, not just a demo

Beyond standard formatting (bold/italic/underline, headings, lists, alignment, RTL), a few features were designed specifically around real product needs:

  • **Markdown shortcuts** — typing `# `, `> `, `- ` auto-converts to a heading, blockquote, or list — useful for users who prefer typing over reaching for toolbar buttons
  • **Bubble toolbar** — a small floating formatting bar appears right above the selected text, cutting down on eye travel to a fixed top toolbar
  • **@mention autocomplete** — configurable via an `onSearch` callback, used to tag other users or link to an entity in the system (a ticket, a document) right inside the content being written
  • **Find & Replace** with regex mode (Ctrl+F/Ctrl+H) — often missing in lightweight editors, but important for longer documents
  • **Inline image cropping** and drag-to-resize for images/videos directly inside the editing area, with no need for an external tool
  • **Auto-save to localStorage** with a restore banner — avoids losing content when a tab is closed by accident or the browser crashes

A specific design decision: rather than cramming every feature into the core, the less-common ones (the FontAwesome icon picker, advanced tables with cell merging, Prism.js syntax highlighting) can all be disabled via the toolbar configuration — keeping the bundle small for products that don't need them.

Sanitization — the part you can't skip in a content-editable component

Any component that accepts HTML input from a user is a potential XSS attack surface — pasting a snippet of HTML containing a `<script>` tag or an `onerror` attribute from an external source is the most common scenario. Autumn Note runs a DOM-based sanitizer at several points: when content is pasted in from outside, when `setHTML()` loads existing content, and before export. The sanitizer strips `<script>`, `<object>`, `<embed>` tags, every `on*` event handler attribute, and blocks `javascript:`/`data:` URLs in href/src — while still preserving iframes from a trusted host allowlist (so legitimate YouTube/Vimeo embeds keep working) instead of bluntly blocking every iframe. This is the same zero-trust principle we apply to security in AI-integrated systems — there's no real difference between content a user pastes into an editor and content fed into an AI agent's context: both need to be treated as untrusted until they pass through an explicit validation/sanitization layer.

Plugin API — extend without forking the core

One lesson from building an editor used across different clients: customization requests are nearly constant, but most of them shouldn't be solved by forking the base code. Autumn Note has a composable plugin API:

  • `name`, `version` — plugin identity
  • `buttons` — register new toolbar buttons, referenced by name in the editor's `toolbar` config
  • `install(ctx, options)` — runs after the editor initializes, with full access to the instance's context API
  • `uninstall(ctx)` — cleanup when the plugin is removed
  • Register globally via `AutumnNote.use(plugin)`, or per instance via `editor.use(plugin)`

This structure lets a client-specific feature (a custom quote block format, or content validation against internal rules) live as a standalone plugin, separate from the core — the core stays small and easy to audit, while the customization lives at the application layer.

Integrating with React and Vue without leaking DOM details

Because Autumn Note manipulates the DOM directly (contenteditable), wiring it carelessly into a React/Vue component easily conflicts with the framework's virtual DOM diffing. The two official wrappers — `autumnnote-react` and `autumnnote-vue` — solve this with an imperative handle: the component exposes a ref with methods (`getHTML()`, `setHTML()`, etc.) instead of letting the framework try to manage the editor's internal content as regular state. The application only interacts through the ref — no manual DOM manipulation, no re-render conflicts.

Why a software services company maintains an open-source library

Autumn Note isn't a commercial product — it's an internal tool we reuse across Pilot Builds that need content editing, much like the internal portal that unified 4 legacy systems needed a shared document/knowledge-base editing layer for 14 teams. Open-sourcing it brings two concrete benefits: first, a component used and tested across multiple real projects is more trustworthy than one written once for a single client and never touched again; second, it's the most transparent way to demonstrate engineering capability — the code, the tests, and the architecture decisions are all public on GitHub, not just a line of description on a services page.

Conclusion

Autumn Note solves a small but recurring problem: products need a formatted text editor that has enough real-world features, doesn't drag in unnecessary dependencies, and is safe against untrusted content by design. Try the demo at autumn.konexforge.com, install it via `npm install autumnnote`, or read the full source on GitHub — it's also the kind of component we reuse within the Development layer of Pilot Builds that need a reliable content-editing layer without rebuilding one from scratch.

Related articles

Development

Per-trip e-invoicing for taxi fleets: a real-time system architecture

Vietnam's Circular 32/2025/TT-BTC requires electronic invoices to be issued at the exact moment a trip ends. This article breaks down three real technical challenges and the event-driven architecture that solves them — from mechanical taximeters to digital signing and automated reconciliation.

Development

GitOps and CI/CD for ML pipelines: from notebook experiment to production deployment with Argo CD

ML pipelines have a different development lifecycle from traditional software: code changes less frequently, models and data change more. Applying software GitOps directly to ML without adjustment typically produces pipelines that are heavy, slow, and hard to debug. How to design CI/CD for ML with Argo CD: decouple model artifacts from code deployments, trigger pipelines when a new model finishes training, and roll back automatically when metrics degrade.

Development

Security in the age of AI: new risks and zero-trust principles for production systems

AI agents and LLMs integrated into production systems open up a new attack surface — prompt injection, data poisoning, deepfakes — adding to, not replacing, traditional security risks. Zero-trust principles, secure SDLC, and a practical checklist for protecting AI-enabled systems.

Have a similar problem to solve?

Contact the team