Turn off SuperDoc’s built-in chrome, listen for the active control, and anchor your own UI over it. The control wrappers andDocumentation Index
Fetch the complete documentation index at: https://docs.superdoc.dev/llms.txt
Use this file to discover all available pages before exploring further.
data-sdt-* attributes stay in the DOM, so your UI has something to attach to.
A minimal field chip
getRect tells you where to draw. active is an SdtRef with id, tag, alias, controlType, and scope.
Pick the right surface
| Goal | API |
|---|---|
| Active control (enter, switch, leave) | superdoc.on('content-control:active-change') |
| Click inside a control | superdoc.on('content-control:click') |
| Full live list and active stack | ui.contentControls.observe() / getSnapshot() |
| Read one control | ui.contentControls.get({ id }) |
| Position your UI | ui.contentControls.getRect({ id }) |
| Scroll a control into view | ui.contentControls.scrollIntoView({ id }) |
| Scroll to it and put the cursor in | ui.contentControls.focus({ id }) |
| Re-anchor your UI when the page moves | ui.viewport.observe(() => ...) |
| Hover and right-click hit-testing | ui.viewport.entityAt() / contextAt() |
| Change content, tags, or locks | editor.doc.contentControls.* |
active is the innermost control. For nested controls (an inline field inside a block clause), activePath carries the full stack, innermost first, so you don’t also need observe() just to read the nesting.
scrollIntoView resolves the control’s position from the document, so it works even when the control is on a page that hasn’t rendered yet (the page mounts, then scrolls). It scrolls only - it does not move the cursor into the control. focus does both: scrolls to the control and places the caret inside so the user can start typing. focus is selection, not editing - it does not bypass lock or document-mode rules, so a locked or read-only control can be focused for inspection but edits are still blocked.
ui.viewport.observe is the single signal for “your getRect() coordinates may be stale, re-query”: it fires (coalesced, once per frame) on scroll, resize, zoom, and layout reflow, so an overlay anchored with getRect stays glued without hand-wiring those events yourself.
How the model works
You build your UI over the control, not inside it. SuperDoc owns how the control’s content is painted in the document; you turn off its built-in chrome and draw your own (chips, badges, panels) anchored withgetRect, react with the events, and change content through editor.doc.contentControls.*. Custom field types are expressed as a tag - for example { kind: 'smartField', key: 'party_name' }, interpreted by your own UI - the underlying control stays a standard Word SDT so it round-trips to .docx.
See also
- Contract templates demo - a working field chip built on these APIs.
- Configuration - the
modules.contentControls.chromeoption. - Document API: content controls - read and change controls.

