DOMD and @do-md/core-react: a Markdown-native editor kernel for streaming AI output and large files
30KB Markdown-native WYSIWYG editor for React, built for AI streaming, human editing, huge files, macOS, Web, and agent workflows.
At a glance
- What is it?
- DOMD wraps @do-md/core-react, a from-scratch Markdown editor kernel that keeps the Markdown document as the editing source of truth. It targets streaming AI output, 20,000-line files, and paragraph-level CRDT merge, with a 30 KB Brotli core and React plus Immer as the only runtime dependencies.
- Who is it for?
- Adopt @do-md/core-react if your product renders Markdown that arrives token by token or documents that run to thousands of lines, and you want the Markdown source itself to be the editing model rather than a serialization step behind a rich-text tree.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem DOMD targets: Markdown that arrives faster than a human can type
Two workloads break most Markdown editors. The first is a model emitting Markdown token by token, splitting syntax across chunk boundaries: an open code fence, a half-built table, a list item with no terminator yet. The second is a file with 20,000 lines in it, where a full re-render on every keystroke is visible as lag or flicker. DOMD is built for both, and the README states the kernel stays smooth through 20,000-line Markdown documents and renders streaming chunks at any chunk size. Those are the project's own claims; nothing here was measured independently. The audience is narrower than "anyone who edits Markdown". It is React developers embedding an editor in a product, and people who want a native macOS app or a local-first web editor for the same documents. The repository also lists a CLI aimed at agent workflows, though the README excerpt does not document its commands.
Markdown as the editing source of truth, not a serialization target
The kernel is not built on ProseMirror, Slate, or Lexical. Parsing, rendering, editing, undo/redo, streaming injection, and chunked file loading are modeled as deterministic state changes inside the kernel, with the Markdown document itself as the editing source of truth. That is a different data flow from the common arrangement, where an editor holds a rich-text tree and Markdown is produced on export. Here the Markdown is the model, and rendering happens only where changes occur. The practical consequence is that round-tripping is not a separate concern: there is no conversion step that can normalize or drop syntax you did not anticipate. The cost is that the kernel has to own parsing and incremental rendering itself, which is why the project describes the engine as from-scratch. The README puts the stack at just over 30 KB after Brotli compression, with React and Immer as the only runtime dependencies.
Inline syntax as a kernel extension point, with Pandoc-style attributes
Most Markdown tools hit a wall when inline syntax needs to grow. Highlights, mentions, comments, and wikilinks end up requiring preprocessing, a forked parser, or raw HTML. In @do-md/core-react 0.6, inline syntax is an extension point of the kernel. The README gives three forms: ==highlight== for a plain highlight, =={red}highlight== for a tinted one via a positional parameter, and =={.comment author="Alice"}highlight== for a semantic type with attributes. Parameters follow the Pandoc and Djot inline-attribute family, which the README describes as a convention shared by Pandoc, Quarto, kramdown, and markdown-it. Delimiters carry no meaning of their own: a .word selects a registered variant, and the README states the same variant can be attached to whichever delimiters fit the product, giving =={.mention id=1}Alice== as equivalent to <{.mention id=1}Alice>. Unregistered types do not error; they degrade into plain CSS hooks. A variant can also bind a React component, which receives the parsed parameters and children and renders inside the live document, with an attribute such as id acting as a stable reference to application data. The README mentions an issue card with an approval action and a refreshing weather widget as examples. This is the most opinionated part of the design. Treating inline syntax as a declarative API with a render contract is a real capability, but it also means the safety of the caret, selection, and collaboration machinery depends on that contract being respected by whatever component you bind. The README asserts the contract exists; it does not spell out its rules in the excerpt.
CRDT merge and realtime sync sit behind an adapter, not in the core
The kernel is CRDT-agnostic. It emits a structured operation stream for ordinary edits, and an optional CRDT plugin observes that stream, translates changes into transactions on nested Yjs shared types, and maintains a mergeable Y.Doc replica. Yjs encodes the replica as document updates that can be persisted, transferred, and applied in any order. The merge granularity is the notable part: the README states the kernel merges within a paragraph rather than treating each paragraph as a last-write-wins value, so two devices editing different parts of the same paragraph offline can exchange saved states later and keep both changes. Realtime synchronization reuses the same foundation. Three integration points are named: subscribeRenderDataOps emits local editing operations, applyExternalRenderDataOps applies remote operations incrementally, and cursor snapshots and subscriptions expose presence data. The optional realtime-sync adapter translates between those APIs and nested Yjs shared types. Incoming changes are replayed onto affected nodes rather than replacing the document. Because the CRDT boundary is an adapter around the operation stream, the README states that product and interaction code does not need to be rebuilt around Yjs, and a finished editor feature can opt in by attaching the plugin. That is a clean separation, and it is also the main integration risk: the adapter is something you add and configure, not something that works by default.
Getting it running: the npm package, the demos, and the macOS builds
The kernel is published as @do-md/core-react on npm, which is the entry point for embedding it in a React application. The README points to isolated demos for individual capabilities, including a split-screen CRDT merge playground at domd.app/playground/crdt and a realtime sync playground at domd.app/playground/live, plus a web editor at domd.app/editor. For the desktop product, the README offers direct download links for the macOS builds: DOMD_aarch64.dmg for Apple Silicon and DOMD_x86_64.dmg for Intel, both under the latest release tag on GitHub. The README also mentions a Quick Look preview and an agent-friendly CLI, but the excerpt does not give commands, flags, or configuration keys for either. Of the API surface, only three names appear with any specificity: subscribeRenderDataOps, applyExternalRenderDataOps, and the realtime-sync adapter. If you need a config key reference or a CLI synopsis, this README excerpt does not supply one, and the repository would have to be checked directly. The most recent releases listed are v0.8.1 and v0.8.0, both dated 2026-08-30, with v0.7.1 on 2026-08-27.
Where DOMD is the wrong tool
The from-scratch engine is the project's main asset and its main constraint. There is no ProseMirror, Slate, or Lexical plugin ecosystem to draw on. If your editor needs a mature set of third-party extensions, or a large body of community examples for a specific editing behaviour, those do not exist here in the same form, and the inline-variant API is the extension path you would be using instead. The CRDT and realtime paths are opt-in adapters over an operation stream, so a team expecting a turnkey collaboration layer will be writing the attachment and configuration themselves, and the README excerpt does not document the adapter's options. The licence is the other hard boundary. The repository reports NOASSERTION, which means GitHub could not map the licence file to a recognized identifier. That is not the same as having no licence, and it is not the same as having a permissive one. It means the terms are not declared in a form the tooling recognizes, and anyone shipping this in a commercial product needs to read the actual licence file before adopting, not after. Nothing here is legal advice, and the only reliable source is the file in the repository.
The alternative: a rich-text tree with Markdown on export
The obvious comparison is an editor built on a general-purpose rich-text framework, most commonly ProseMirror or Lexical with a Markdown serializer. The difference in approach is where the source of truth lives. In that model the editor holds a document tree of typed nodes, and Markdown is produced when you export or save. Syntax the tree does not model gets normalized, dropped, or pushed into a code block or raw HTML escape hatch. In DOMD the Markdown is the model, so syntax the kernel does not understand is still the document, and the incremental rendering described in the README applies to changes in that Markdown rather than to tree mutations. The trade runs both ways. The tree model gives you a large plugin ecosystem, established selection and input handling, and a well-known mental model for extension authors. The Markdown-native model gives you round-trip fidelity and a smaller surface, at the cost of building extensions against a younger API. Neither is universally better; the choice follows from whether your product treats Markdown as the artifact or as an interchange format.
Maintenance, upgrade cost, and the licence question
The release cadence visible in the supplied material is tight: v0.7.1 on 2026-08-27, then v0.8.0 and v0.8.1 both on 2026-08-30, and the last push to the repository on 2026-09-10. A version line that moves from 0.7 to 0.8 in three days suggests active development, and it also suggests that minor versions can carry API movement. The README itself ties a capability to a version, describing inline syntax as a first-class extension point in @do-md/core-react 0.6, which is a hint that the API has changed across releases. For a team embedding the kernel, that means pinning a version and reading release notes before upgrading rather than tracking latest. The dependency footprint is small, React and Immer only, so the usual transitive-dependency maintenance burden is limited by design. On licensing, the repository's NOASSERTION status is the item to resolve first. A permissive licence and a copyleft one imply very different integration costs for a closed-source product, and the repository metadata as supplied does not tell you which applies. Read the licence file, and if the terms are unclear, treat that as a blocker rather than a detail.
Editorial conclusion
Adopt @do-md/core-react if your product renders Markdown that arrives token by token or documents that run to thousands of lines, and you want the Markdown source itself to be the editing model rather than a serialization step behind a rich-text tree. Do not adopt it if you need a mature plugin ecosystem, if your licence review requires an OSI identifier today, or if you expect an out-of-the-box solution for the CRDT and realtime-sync paths, which the README presents as optional adapters you attach yourself. Before committing, verify three things against the repository: the actual contents of the licence file behind the NOASSERTION status, whether the v0.8.1 package on npm matches the API names in the README (subscribeRenderDataOps, applyExternalRenderDataOps, realtime-sync), and whether the streaming and large-file behaviour holds for your own chunk sizes and file sizes, since the README's performance statements are the project's own and not independently measured here.
Community notes