docs/plans/2026-04-26-slate-v2-custom-placeholder-visual-parity.md
Compare examples/custom-placeholder against legacy Slate and fix the visual regression where custom placeholders render as normal black document content instead of the grey absolute overlay.
renderPlaceholder attributes.SlatePlaceholder.renderPlaceholder in v2 receives attributes.style = {}, so example markup falls into normal document flow.dev-browser showed a second legacy-parity miss: the placeholder was absolutely positioned, but the editable root stayed 22px tall while the custom placeholder was 86px tall. The overlay was vertically outside the editor.packages/slate-react/src/components/editable-text.tsx for placeholder attrs and packages/slate-react/src/components/editable-text-blocks.tsx for placeholder-height measurement, not the example.slate-react.bun test ./packages/slate-react/test/primitives-contract.tsx -t "custom placeholder" --bail 1 failed with position empty.bun test ./packages/slate-react/test/primitives-contract.tsx --bail 1 passed.bun test ./packages/slate-react/test/rendered-dom-shape-contract.tsx --bail 1 passed.bunx turbo build --filter=./packages/slate-react --filter=./packages/slate-dom --force passed.bunx turbo typecheck --filter=./packages/slate-react --filter=./packages/slate-dom --force passed.bun run lint:fix passed and formatted one file.bun run lint passed.dev-browser --connect http://127.0.0.1:9222 verified /examples/custom-placeholder computes position: absolute, opacity: 0.333, and pointer-events: none for [data-slate-placeholder="true"].dev-browser verification caught the height regression: editor height 22.390625, placeholder height 86.578125, bottomInside: false.custom placeholder height contributes to editable root height in rendered-dom-shape-contract.tsx.dev-browser verification: editor height 86.578125, placeholder height 86.578125, root minHeight: 86.5781px, and all containment checks true.docs/solutions/logic-errors/2026-04-04-v2-placeholder-primitives-should-own-overlay-attrs-and-style.md with the custom renderer regression and shared-style rule.Status: done.
Symptom:
/examples/custom-placeholder and deleting it all, v2
rendered only the custom placeholder body. The Type something placeholder
child disappeared, and the editor root collapsed back to one empty line.Cause:
placeholderValue = undefined. Deleting back to empty only rerendered the
text node, so the custom renderer was called with children: undefined, and
placeholder height measurement never restarted.Fix:
EditableTextBlocks now subscribes to the placeholder-visible state instead
of deriving it only during structural root renders.EditableText no longer calls a custom renderPlaceholder when no
placeholder value is present.Evidence:
bun test ./packages/slate-react/test/rendered-dom-shape-contract.tsx -t "custom placeholder restores" --bail 1 failed with placeholder text custom placeholder.bun test ./packages/slate-react/test/rendered-dom-shape-contract.tsx --bail 1bun test ./packages/slate-react/test/primitives-contract.tsx --bail 1bun --cwd packages/slate-react test -- --bail 1bunx turbo build --filter=./packages/slate-react --forcebunx turbo typecheck --filter=./packages/slate-react --forcebun run lint:fixbun run lintdev-browser --connect http://127.0.0.1:9222 verified type/delete on
http://localhost:3100/examples/custom-placeholder: placeholder text
restored, editor height 86.578125, placeholder height 86.578125,
minHeight: 86.5781px, position: absolute, and bottomInside: true.