docs/solutions/logic-errors/2026-04-01-markdown-blockquotes-must-round-trip-as-container-blocks.md
Plate's markdown layer treated blockquotes like flat text blocks even though the editor model already allows blockquotes to wrap block children.
That mismatch broke real markdown content. Input like:
Hello!
> some thing is reference
> - aaa
> - bbb
lost the nested list because the blockquote seam collapsed everything into text before the list structure could survive.
Make blockquote a real container contract across the whole surface:
tf.blockquote.toggle() to wrap and unwrap blocksBaseBlockquotePluginThe critical deserialize seam became:
const children = groupInlineChildrenIntoParagraphs(
editor,
convertNodesDeserialize(mdastNode.children, deco, options)
);
return [
{
children,
type: editor.getType(KEYS.blockquote),
},
];
The transform seam changed from block replacement to wrapper semantics:
toggle: () => {
editor.tf.toggleBlock(type, { wrap: true });
};
The bug was never just "lists inside blockquotes." The real problem was that markdown, plugin transforms, and docs disagreed about what a blockquote is.
Once blockquote is treated as a container everywhere, nested paragraphs and lists survive deserialize, serialize, seeded values, and user transforms on the same shape.
These checks passed:
bun test packages/markdown/src/lib/deserializer/deserializeMd.spec.ts packages/markdown/src/lib/deserializer/deserializeMdList.spec.tsx packages/markdown/src/lib/serializer/convertNodesSerialize.spec.ts packages/basic-nodes/src/lib/BaseBlockquotePlugin.spec.ts apps/www/src/__tests__/package-integration/markdown-deserializer/deserializeMd.slow.tsx apps/www/src/__tests__/package-integration/markdown-deserializer/deserializeMdParagraphs.spec.tsx
pnpm turbo build --filter=./packages/markdown --filter=./packages/basic-nodes
pnpm turbo typecheck --filter=./packages/markdown --filter=./packages/basic-nodes
pnpm lint:fix
#4898#4831