Skip to content

Dynamic Marks

Dynamic marks are custom components rendered for parsed mark tokens. Use useMark() inside a Mark component to read the current mark snapshot and issue commands through the core value pipeline.

import {useMark} from '@markput/react'
function Mention() {
const mark = useMark()
return (
<span>
@{mark.value}
<button type="button" onClick={() => mark.remove()}>
Remove
</button>
</span>
)
}

useMark() returns a MarkController:

Property or methodPurpose
valueCurrent __value__ snapshot.
metaCurrent __meta__ snapshot.
slotCurrent __slot__ raw content snapshot.
readOnlyWhether the editor is read-only for this snapshot.
update(patch)Serialize a patch and replace the mark raw range.
remove()Delete the mark raw range.

The controller does not expose a DOM ref. React and Vue own structural DOM and register it privately with core. Keyboard focus and caret recovery are handled by store.dom and store.caret.

function EditableMention() {
const mark = useMark()
return (
<button type="button" onClick={() => mark.update({value: 'updated'})}>
@{mark.value}
</button>
)
}

Optional fields use explicit set/clear patches:

mark.update({meta: {kind: 'set', value: 'user:1'}})
mark.update({meta: {kind: 'clear'}})
mark.update({slot: {kind: 'set', value: 'nested text'}})

All commands go through store.value.replaceRange(). In controlled mode, Markput emits onChange and waits for the matching value prop echo before applying recovery.

function RemovableMark() {
const mark = useMark()
return (
<button type="button" disabled={mark.readOnly} onClick={() => mark.remove()}>
{mark.value}
</button>
)
}

remove() and update() return an edit result. A read-only editor returns {ok: false, reason: 'readOnly'}.

Use useMarkInfo() for structural information. This keeps commands separate from debug/layout metadata.

import {useMark, useMarkInfo} from '@markput/react'
function NestedAwareMark({children}: {children?: React.ReactNode}) {
const mark = useMark()
const info = useMarkInfo()
return (
<span data-depth={info.depth} data-key={info.key}>
{info.hasNestedMarks ? children : mark.value}
</span>
)
}

useMarkInfo() returns address, depth, hasNestedMarks, and key.