docs/analysis/slate-v2-plate-v2-architecture-research.md
This is the cumulative research ledger for architecture ideas gathered from external editor and non-editor references.
The priority order is:
slate-v2plate-v2 ideas without letting them distort slate-v2These constraints are not under review:
slate-v2 is data-model-firstslate-react-v2 is intentionally React 19.2+ and React-perfectThat means:
Every idea should land in exactly one bucket:
adopt-now-for-slate-v2adopt-later-for-slate-v2better-fit-for-plate-v2interesting-but-rejectAlready proven in .tmp/slate-v2:
slate-v2slate-dom-v2slate-react-v2slate-history-v2Still missing structurally:
Status: completed
Classification:
adopt-later-for-slate-v2Reason:
slate-v2 core is already stronger on transaction discipline and history directionClassification:
adopt-now-for-slate-v2Reason:
slate-dom-v2 directionuseEffect, useRef, and useStateClassification:
interesting-but-rejectReason:
slate-react-v2Classification:
interesting-but-rejectReason:
slate-history-v2 directionClassification:
adopt-now-for-slate-v2Reason:
slate-v2 structural seam:
explicit clipboard-boundary ownershipapply and mount plugin hooks in plugins/types.tsClassification:
better-fit-for-plate-v2Reason:
Classification:
interesting-but-rejectEdix is a good adapter-and-clipboard reference.
It is not the renderer blueprint and not the history blueprint.
Status: covered-in-baseline
Summary:
slate-v2 planning already came from direct Slate inheritance pressure plus the issue corpusStatus: completed
modeltransformstateviewhistorycollabEditorState is persistent and immutable in state.tsTransaction is a first-class state update object in transaction.tsClassification:
adopt-now-for-slate-v2Reason:
slate-v2 focused on model plus transactions instead of collapsing runtime concerns back inwardview is explicitly separate from state and transformClassification:
adopt-now-for-slate-v2Reason:
slate-dom-v2 package boundaryClassification:
interesting-but-rejectReason:
slate-react-v2 renderer modelhistory is its own packagehistoryPreserveItems contract for collab rebasingClassification:
adopt-later-for-slate-v2Reason:
slate-history-v2 proof is simpler and right-sized for nowcollab is its own packageClassification:
adopt-later-for-slate-v2Reason:
view, not in core statedata-pm-slice) rather than accidental format leakagetransformCopiedclipboardSerializerclipboardTextSerializertransformPastedTextclipboardTextParserclipboardParsertransformPastedHTMLtransformPastedClassification:
adopt-now-for-slate-v2Reason:
filterTransactionappendTransactioninit / applyviewClassification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
Classification:
interesting-but-rejectProseMirror is the strongest external validation of the package split and the strongest direct reference for the next clipboard-boundary seam.
It does not change our React runtime direction. It does make the package architecture look even more correct.
Status: completed
lexical@lexical/react@lexical/history@lexical/clipboard@lexical/selection@lexical/extension@lexical/headlessClassification:
adopt-later-for-slate-v2Reason:
slate-v2 constraints to copy directly@lexical/react is a dedicated package, not an afterthoughtLexicalComposer creates the editor and owns provider setup in LexicalComposer.tsxOnChangePlugin filters update notifications by tags and dirty sets in LexicalOnChangePlugin.tsClassification:
adopt-now-for-slate-v2interesting-but-rejectReason:
useLexicalSubscription still uses useState plus useLayoutEffect, not useSyncExternalStore19.2+ renderer direction@lexical/history is its own packageHISTORY_MERGE_TAG, HISTORY_PUSH_TAG, and HISTORIC_TAGClassification:
adopt-later-for-slate-v2Reason:
slate-history-v2 proof is intentionally smaller@lexical/clipboard is its own packageClassification:
adopt-now-for-slate-v2Reason:
@lexical/selection is its own packageClassification:
adopt-later-for-slate-v2Reason:
slate-dom-v2 proof already split DOM selection ownership correctly@lexical/extension is its own packageLexicalBuilder composes extensions and dependencies explicitly in LexicalBuilder.tsClassification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
plate-v2 than to the next core Slate package seam@lexical/headless is a real package, not a side effectClassification:
better-fit-for-plate-v2Reason:
Lexical is the strongest validation of:
It does not beat the current slate-react-v2 decision to use useSyncExternalStore and React 19.2+ as the target runtime model.
Status: completed
@tiptap/core is a ProseMirror wrapper, not a fresh editor engine, in Editor.ts@tiptap/pm repackages the ProseMirror module set behind one dependency surface in package.jsonClassification:
better-fit-for-plate-v2Reason:
slate-v2 core around someone else’s wrapperEditorView, just behind Tiptap’s wrapper in Editor.tsClassification:
interesting-but-rejectReason:
@tiptap/react is a dedicated runtime package in package.jsonuseEditor uses an instance manager plus useSyncExternalStore shim in useEditor.tsuseEditorState uses useSyncExternalStoreWithSelector in useEditorState.tsClassification:
adopt-later-for-slate-v2interesting-but-rejectReason:
slate-react-v2Classification:
interesting-but-rejectClassification:
interesting-but-rejectExtension base class in Extension.tsExtensionManager composition of commands, keymaps, input rules, paste rules, ProseMirror plugins, node views, and dispatch middleware in ExtensionManager.tsClassification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
plate-v2Classification:
better-fit-for-plate-v2Reason:
plate-v2Classification:
interesting-but-rejectTiptap is fast mostly because it packages ProseMirror extremely well.
That matters a lot for plate-v2.
It matters much less for slate-v2, except as a reminder not to confuse product packaging wins with engine/runtime wins.
Status: completed
prepare(...) for one-time segmentation and measurementlayout(...) for cheap repeated width/height calculationClassification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
slate-v2 package plangetBoundingClientRect rouletteClassification:
better-fit-for-plate-v2Reason:
slate-dom-v2 workClassification:
interesting-but-rejectClassification:
interesting-but-rejectClassification:
interesting-but-rejectClassification:
interesting-but-rejectprepareWithSegments, layoutWithLines, walkLineRanges, and layoutNextLine expose deterministic line-fitting primitives in README.mdClassification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
slate-v2 runtime stackPretext is a serious measurement primitive.
It strengthens the case for deterministic layout math above the editor, not inside it.
Useful later for virtualization and layout-heavy systems.
Not a reason to distort the current slate-v2 package plan.
Status: completed
UnmeasuredDocumentSnapshotMeasuredDocumentSnapshotLayoutInputLayoutOutputMappingIndex
in packages/core/src/index.tsClassification:
adopt-now-for-slate-v2Reason:
Classification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
slate-dom-v2 scopesnapshot -> measure -> compose in usePremirrorEngine and renders page viewports plus projected selections in packages/react/src/index.tsxcontenteditable root stays authoritativeClassification:
better-fit-for-plate-v2Reason:
slate-react-v2Classification:
interesting-but-rejectClassification:
interesting-but-rejectClassification:
better-fit-for-plate-v2Reason:
EditorState -> snapshot -> measure (pretext) -> compose -> LayoutOutputClassification:
better-fit-for-plate-v2Reason:
slate-v2, likely in future plate-v2 or a sibling layout stackPremirror is not “better Slate.”
It is the best evidence so far that page-aware editing should be built as:
That architecture sharpens the slate-v2 plan by keeping pagination out of the core, and it sharpens the future plate-v2 plan by giving it a serious high-layer target shape.
Status: completed
use-editable is not an engine. It is a tiny hook that turns a single contenteditable element into a text surface in useEditable.tsClassification:
better-fit-for-plate-v2Reason:
slate-v2 model/runtime stackRange helpers instead of model-backed mappingClassification:
interesting-but-rejectReason:
slate-v2 constraintsuseLayoutEffect, not an external-store model, in useEditable.tsEdit handle is explicitly imperative: update, insert, move, getStateClassification:
interesting-but-rejectReason:
slate-react-v2 targetClassification:
interesting-but-rejectReason:
Classification:
interesting-but-rejectClassification:
interesting-but-rejectonChangeClassification:
better-fit-for-plate-v2Reason:
Classification:
interesting-but-rejectuse-editable is a useful lower bound and a useful warning.
It shows how small a React editing surface can be.
It also shows exactly why that trickbox should not be mistaken for the main architecture of slate-v2.
Status: completed
rich-textarea is not an editor engine. It is a decorated native textarea surface with a mirrored render layer in textarea.tsxClassification:
better-fit-for-plate-v2Reason:
slate-v2 stack<textarea> for input truth in textarea.tsxResizeObserver and cloned mouse events in observer.tsClassification:
better-fit-for-plate-v2Reason:
Classification:
interesting-but-rejectReason:
slate-react-v2Classification:
interesting-but-rejectClassification:
interesting-but-rejectchildren render functionClassification:
better-fit-for-plate-v2Reason:
Classification:
better-fit-for-plate-v2Reason:
plate-v2 taxonomy more than slate-v2 architectureClassification:
interesting-but-rejectrich-textarea is the strongest lightweight-surface reference so far.
Its lesson is simple:
Status: completed
markdown-editor is not an engine. It is a markdown-specific contenteditable projection layer over a string model in MarkdownEditor.tsxunified and turned into React nodes in MarkdownCompiler.tsClassification:
interesting-but-rejectReason:
display:block breaks layout assumptions
in README.mdClassification:
interesting-but-rejectReason:
use-local-event is interesting, but the core runtime is still imperative and DOM-drivenClassification:
better-fit-for-plate-v2interesting-but-rejectReason:
slate-v2Classification:
interesting-but-rejectReason:
slate-history-v2Classification:
interesting-but-rejectClassification:
better-fit-for-plate-v2Reason:
MarkdownEditorClassification:
interesting-but-rejectReason:
Classification:
interesting-but-rejectmarkdown-editor is more warning than blueprint.
Useful lesson:
So the lightweight-surface lane should bias toward rich-textarea-style native-control overlays, not markdown-specific contenteditable projection hacks.
Status: completed
@tanstack/db is a normalized collection store with explicit query builder, live-query collections, indexes, and optimistic mutation seams in index.ts@tanstack/db-ivm in compiler/index.ts and db-ivm/README.mdClassification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
slate-v2 coreClassification:
interesting-but-rejectuseSyncExternalStore-style live-query subscription in useLiveQuery.tsClassification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
Classification:
interesting-but-rejectClassification:
interesting-but-rejectClassification:
better-fit-for-plate-v2Reason:
Classification:
better-fit-for-plate-v2Reason:
Classification:
interesting-but-rejectTanStack DB is the best current reference for:
That is powerful future plate-v2 pressure.
For slate-v2, it is only a later import for derived projection/index layers, not a core architectural pivot.
Status: completed
urql is a client/event hub built around keyed operations and results in client.tsClassification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
Classification:
interesting-but-reject@urql/core, as seen in index.tsClassification:
better-fit-for-plate-v2Reason:
slate-react-v2Classification:
interesting-but-rejectClassification:
interesting-but-rejectClassification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
urql keeps one core and many thin wrappers, with optional exchanges and optional normalized cacheClassification:
better-fit-for-plate-v2Reason:
Classification:
interesting-but-rejecturql is the best exchange-pipeline import so far.
The reusable lessons are:
That should influence future semantic and extension architecture far more than base editor runtime.
Status: completed
Classification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
slate-v2 coreClassification:
interesting-but-rejectClassification:
interesting-but-rejectClassification:
interesting-but-rejectClassification:
interesting-but-rejectClassification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
Classification:
better-fit-for-plate-v2Reason:
slate-v2 itselfClassification:
interesting-but-rejectVS Code is the strongest reference so far for semantic feature architecture inside a tool:
That should influence future plate-v2 semantics much more than slate-v2 runtime.
Status: completed
Classification:
better-fit-for-plate-v2Reason:
Classification:
interesting-but-rejectClassification:
interesting-but-rejectClassification:
interesting-but-rejectClassification:
interesting-but-rejectcompletionItem/resolve for expensive details in completion.mdContentModified handling for stale requests in specification-3-14.mdClassification:
better-fit-for-plate-v2Reason:
Classification:
better-fit-for-plate-v2Reason:
Classification:
interesting-but-rejectLSP’s main lesson is brutal and useful:
That is a future plate-v2 architecture lesson, not a reason to complicate slate-v2.
Status: completed
Classification:
adopt-later-for-slate-v2better-fit-for-plate-v2Reason:
EditContext, not the DOMtextupdate and textformatupdatebeforeinput intent
in explainer.mdEditContext is active the user agent must not directly update the DOM, fire input, or fire composition events on the host as a direct result of user actionClassification:
adopt-later-for-slate-v2Reason:
slate-dom-v2 input ownershipClassification:
interesting-but-rejectClassification:
interesting-but-rejectReason:
Classification:
interesting-but-rejectReason:
Classification:
interesting-but-rejectClassification:
better-fit-for-plate-v2Reason:
Classification:
interesting-but-rejectEditContext is real and interesting.
It is not magic. It can decouple text input from DOM mutation, but it does not replace:
So it is a later slate-dom-v2 candidate and a future custom-surface tool, not the answer to today’s structural seams.
Status: completed
<input> and <textarea> much more capable in richer-text-fields.explainer.mdxInputRange-style range extractionClassification:
better-fit-for-plate-v2Reason:
slate-v2 rich-document corecontenteditable for many richer text-field use cases in richer-text-fields.explainer.mdxInputRange() as a boundary layer for live input/textarea valuesHTMLInputElement / HTMLTextAreaElementmask attributeClassification:
better-fit-for-plate-v2Reason:
rich-textarea-style surfaces get dramatically simpler and sturdierClassification:
interesting-but-rejectClassification:
better-fit-for-plate-v2Reason:
Classification:
better-fit-for-plate-v2Reason:
Classification:
interesting-but-rejectcontenteditable or full editor frameworksClassification:
better-fit-for-plate-v2Reason:
Classification:
interesting-but-rejectOpen UI Richer Text Fields strengthens one conclusion hard:
contenteditable or Slate for every richer text interactionSo this is a strong future plate-v2 action area, not a slate-v2 architectural pivot.