docs/solutions/logic-errors/2026-04-06-v2-html-paste-formatting-should-stay-app-owned-on-explicit-inline-elements.md
The next honest Phase 8 expansion for paste-html was a small formatting set:
strong, em, and code.
The dangerous version of that work was obvious too:
slate-domThat would have spent proof budget in the wrong layer.
paste-html could paste a paragraph with a link, but
<strong>, <em>, and <code> disappeared.EditableBlocks seam could render app-owned inline elements
cleanly, but did not expose text-node mark metadata through renderSegment.slate-react needs general mark rendering now”.
That would have widened the runtime seam for one example-level policy.slate-dom clipboard problem. The browser boundary was
already fine; the missing piece was how the app wanted to represent the pasted
formatting subset.Keep the behavior app-owned and explicit.
The paste-html surface now:
STRONG, EM, and CODE into explicit inline element nodesThat means the supported HTML subset is now:
strongemcodeThe critical seam was:
case 'STRONG':
return [createFormatNode('strong', children)]
case 'EM':
return [createFormatNode('em', children)]
case 'CODE':
return [createFormatNode('code', children)]
and the matching inline renderer:
if (isFormatElement(element)) {
const Tag = getFormatTag(element);
return (
<EditableElement as="span" isInline>
<Tag>{children}</Tag>
</EditableElement>
);
}
This preserves the real package boundary:
slate-react provides the runtime seam for app-owned renderingslate-dom stays the browser transport boundaryslate stays out of formatting policyThe durable insight is that the current EditableBlocks seam is better at
app-owned inline elements than app-owned text marks.
That is because:
renderSegment seam does not carry text-node mark metadataSo the honest move was not “teach the runtime generic marks for one example”. It was “represent the explicit HTML formatting subset as explicit inline elements”.
paste-html, add browser reds for supported tags before changing the
parser or renderer.