Native Word SDT (w:sdt) fields for documents. Supports inline and block structured content tags for dynamic templates with full Word compatibility.
Use case
Form templates — Create fillable documents with inline text fields and block content areas
Contract generation — Dynamic clauses and terms that map to Word content controls
Document automation — Programmatically update specific sections while preserving structure
Protected fields — Lock modes control which fields users can edit or delete (ECMA-376 w:lock)
Quick start
// Insert inline field with sdtLocked — users can edit content but not delete the field
editor . commands . insertStructuredContentInline ({
attrs: {
id: '1' ,
alias: 'Customer Name' ,
lockMode: 'sdtLocked' ,
},
text: 'John Doe'
});
// Insert a read-only system field
editor . commands . insertStructuredContentInline ({
attrs: {
id: '3' ,
alias: 'Account ID' ,
lockMode: 'sdtContentLocked' , // fully protected — no edits, no deletion
},
text: 'ACC-00042'
});
// Insert block field with sdtLocked — content is editable, wrapper is protected
editor . commands . insertStructuredContentBlock ({
attrs: {
id: '2' ,
alias: 'Terms & Conditions' ,
lockMode: 'sdtLocked' ,
},
html: '<p>Please review the terms...</p>'
});
// Update field content
editor . commands . updateStructuredContentById ( '1' , {
text: 'Jane Smith'
});
// Change lock mode without changing content
editor . commands . updateStructuredContentById ( '1' , {
attrs: { lockMode: 'contentLocked' }
});
// Get all structured content tags
const allTags = editor . helpers . structuredContentCommands . getStructuredContentTags ( editor . state );
console . log ( `Document contains ${ allTags . length } SDT fields` );
Options
Configure the extension behavior:
structuredContentClass
string
default: "sd-structured-content-block-tag"
CSS class for the block
HTML attributes for structured content blocks
Attributes
Node attributes that can be set and retrieved:
Unique identifier for the structured content block The id attribute must be a numeric string for valid DOCX output (per
ECMA-376
§17.5.2.18 ).
Use Date.now().toString() or sequential integers instead of UUIDs.
Content control tag (e.g., ‘block_table_sdt’)
alias
string
default: "Structured content"
Display name for the block
lockMode
StructuredContentLockMode
default: "unlocked"
Controls editing and deletion restrictions on the structured content node. See Lock modes below.
Lock modes
Structured content nodes support four lock modes based on the OOXML w:lock element (ISO/IEC 29500 §17.5.2.23). Lock modes control whether users can edit the content inside a field and whether they can delete the field wrapper itself.
Lock mode Wrapper Content Use case unlockedDeletable Editable Default — no restrictions sdtLockedProtected Editable Protect field structure, allow value changes contentLockedDeletable Read-only Display a computed value users can remove sdtContentLockedProtected Read-only Fully protected field (e.g., system-generated ID)
Set the lock mode when inserting:
editor . commands . insertStructuredContentInline ({
attrs: {
id: '3' ,
alias: 'Account ID' ,
lockMode: 'sdtContentLocked' ,
},
text: 'ACC-00042' ,
});
Change the lock mode on an existing field:
editor . commands . updateStructuredContentById ( '1' , {
attrs: { lockMode: 'contentLocked' },
});
Show How lock enforcement works
Lock modes are enforced at the editor plugin level using a three-layer defense:
Key interception — Delete, Backspace, and Cut are blocked before a transaction is created, preventing cursor jumps
Text input blocking — Typing is silently blocked in content-locked nodes
Transaction filter — Safety net that catches paste, drag-drop, and programmatic edits
Users can still place their cursor inside locked content and select text for copying. Only modifications are blocked.
Lock modes round-trip through DOCX. A document with w:lock elements in
its SDT properties will import with the correct lock mode and export the
w:lock element back to the saved file.
Commands
insertStructuredContentInline
Inserts a structured content inline at the current selection.
Example:
editor . commands . insertStructuredContentInline ({
attrs: {
id: '1' ,
alias: 'Customer Name' ,
lockMode: 'sdtLocked' , // optional, defaults to 'unlocked'
},
text: 'John Doe' ,
});
Parameters:
options
StructuredContentInlineInsert
required
insertStructuredContentBlock
Inserts a structured content block at the current selection.
Example:
editor . commands . insertStructuredContentBlock ({
attrs: {
id: '2' ,
alias: 'Terms Section' ,
lockMode: 'sdtContentLocked' , // optional, defaults to 'unlocked'
},
html: '<p>These terms are non-negotiable.</p>' ,
});
Parameters:
options
StructuredContentBlockInsert
required
updateStructuredContentById
Updates a single structured content field by its unique ID.
IDs are unique identifiers, so this will update at most one field.
If the updated node does not match the schema, it will not be updated.
Pass attrs alone to change attributes (like lockMode) without replacing content:
Example:
// Update content
editor . commands . updateStructuredContentById ( '1' , {
text: 'Jane Smith' ,
});
// Update lock mode only (preserves content)
editor . commands . updateStructuredContentById ( '1' , {
attrs: { lockMode: 'contentLocked' },
});
// Update both content and attributes
editor . commands . updateStructuredContentById ( '1' , {
text: 'New value' ,
attrs: { alias: 'Updated Label' , lockMode: 'sdtLocked' },
});
Parameters:
Unique identifier of the field
options
StructuredContentUpdate
required
deleteStructuredContent
Removes a structured content.
Parameters:
deleteStructuredContentById
Removes a structured content by ID.
Parameters:
idOrIds
string | Array<string>
required
deleteStructuredContentAtSelection
Removes a structured content at cursor, preserving its content.
updateStructuredContentByGroup
Update all structured content fields that share the same group identifier.
Example:
editor . commands . updateStructuredContentByGroup ( 'pricing' , {
text: '$99.00'
})
Parameters:
Group identifier to match
Update options (same as updateStructuredContentById)
deleteStructuredContentByGroup
Remove all structured content fields that share the same group identifier.
Example:
editor . commands . deleteStructuredContentByGroup ( 'pricing' )
editor . commands . deleteStructuredContentByGroup ([ 'pricing' , 'deprecated' ])
Parameters:
groupOrGroups
string | Array<string>
required
Group identifier or array of group identifiers
appendRowsToStructuredContentTable
Append multiple rows to the end of a table inside a structured content block.
Each inner array represents the cell values for one new row.
Example:
editor . commands . appendRowsToStructuredContentTable ({
id: "block-123" ,
tableIndex: 0 ,
rows: [
[ "A" , "B" ],
[ "C" , "D" ],
],
copyRowStyle: true ,
});
Parameters:
options
StructuredContentTableAppendRowsOptions
required
Append configuration
Helpers
getStructuredContentBlockTags
Get all block-level structured content tags in the document
Example:
const blocks = editor . helpers . structuredContentCommands . getStructuredContentBlockTags ( editor . state );
console . log ( `Found ${ blocks . length } structured content blocks` );
Parameters:
getStructuredContentInlineTags
Get all inline structured content tags in the document
Example:
const inlines = editor . helpers . structuredContentCommands . getStructuredContentInlineTags ( editor . state );
console . log ( `Found ${ inlines . length } inline fields` );
Parameters:
getStructuredContentTablesById
Find all tables inside a structured content block by ID
Example:
const tables = editor . helpers . structuredContentCommands . getStructuredContentTablesById (
"block-123" ,
editor . state
);
console . log ( `Block contains ${ tables . length } table(s)` );
Parameters:
Structured content block ID
getStructuredContentByGroup
Find all structured content nodes that share the same group identifier.
Example:
const fields = editor . helpers . structuredContentCommands . getStructuredContentByGroup ( 'pricing' , editor . state );
fields . forEach (({ node , pos }) => {
console . log ( node . attrs . tag , pos );
});
Parameters:
groupOrGroups
string | Array<string>
required
Group identifier or array of group identifiers
getStructuredContentTags
Get all structured content tags (inline and block) in the document
Example:
const allTags = editor . helpers . structuredContentCommands . getStructuredContentTags ( editor . state );
console . log ( `Found ${ allTags . length } structured content elements` );
Parameters:
getStructuredContentTagsById
Get structured content tag(s) by ID
Example:
const field = editor . helpers . structuredContentCommands . getStructuredContentTagsById (
"field-123" ,
editor . state
);
if ( field . length ) console . log ( "Found field:" , field [ 0 ]. node . attrs );
Parameters:
idOrIds
string | Array<string>
required
Single ID or array of IDs to find
Types
StructuredContentLockMode
type StructuredContentLockMode = 'unlocked' | 'sdtLocked' | 'contentLocked' | 'sdtContentLocked'
StructuredContentInlineInsert
Node attributes (id, alias, tag, lockMode, group)
StructuredContentBlockInsert
Node attributes (id, alias, tag, lockMode, group)
StructuredContentUpdate
Replace content with text (only for structured content inline)
Replace content with HTML (only for structured content block)
Replace content with ProseMirror JSON (overrides html)
Update attributes only (preserves content). Supports lockMode, alias, tag.
StructuredContentTableAppendRowsOptions
Structured content block identifier
Index of the table inside the block
rows
Array<Array<string>> | Array<string>
required
Cell values to append
Clone the last row’s styling when true
Source code