Skip to content

Nested Marks

Use __slot__ when a mark should contain parsed child tokens.

const options = [{markup: '**__slot__**'}]

A slot mark receives rendered child tokens as children. Render those children to preserve nested content.

function Bold({children}: {children?: React.ReactNode}) {
return <strong>{children}</strong>
}

Use useMarkInfo() for nesting metadata:

import {useMark, useMarkInfo} from '@markput/react'
function TreeMark({children}: {children?: React.ReactNode}) {
const mark = useMark()
const info = useMarkInfo()
return (
<span data-depth={info.depth}>
{info.hasNestedMarks ? children : (mark.slot() ?? mark.value())}
</span>
)
}
Property Purpose
depth Nesting depth, where top-level marks are 0.
hasNestedMarks Whether any child token is a mark.

useMark() walks downward through mark.children(); there is no parent link. Core owns node identity and resolves addresses through the live tree.

React and Vue wrap nested slot content in an internal child-sequence host. Custom Mark components should treat children as opaque rendered content and render it exactly once.

function Highlight({children}: {children?: React.ReactNode}) {
return <mark>{children}</mark>
}

Marks can render controls, icons, or layout elements around children:

function TodoItem({children}: {children?: React.ReactNode}) {
return (
<label>
<input type="checkbox" />
{children}
</label>
)
}

Do not inspect DOM child order to infer token identity. Use useMarkInfo() for structure and useMark() for commands.

If a Mark component ignores children, nested content is not rendered. This is valid for value-only marks, but slot marks should render children or explicitly provide a fallback.

function SlotMark({children}: {children?: React.ReactNode}) {
const mark = useMark()
const info = useMarkInfo()
return <span>{info.hasNestedMarks ? children : mark.slot()}</span>
}