Documentation Index
Fetch the complete documentation index at: https://superdoc-nick-sd-2070-add-content-controls-namespace-to-doc.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Create extensions to add custom features to SuperDoc.
Basic extension
import { Extensions } from 'superdoc';
const { Extension } = Extensions;
const MyExtension = Extension.create({
name: 'myExtension',
addCommands() {
return {
myCommand: () => ({ commands }) => {
console.log('Command executed');
return true;
}
};
}
});
// Use it
editor.commands.myCommand();
Extension types
Node extension
For document elements:
import { Extensions } from 'superdoc';
const { Node } = Extensions;
const CustomBlock = Node.create({
name: 'customBlock',
group: 'block',
content: 'inline*',
parseHTML() {
return [{ tag: 'div[data-custom]' }];
},
renderHTML({ HTMLAttributes }) {
return ['div', { 'data-custom': '' }, 0];
}
});
Mark extension
For inline formatting:
import { Extensions } from 'superdoc';
const { Mark } = Extensions;
const Highlight = Mark.create({
name: 'highlight',
addCommands() {
return {
toggleHighlight: () => ({ commands }) => {
return commands.toggleMark(this.name);
}
};
}
});
Adding features
Commands
addCommands() {
return {
simpleCommand: () => ({ commands }) => {
return commands.insertContent('Hello');
},
complexCommand: (text) => ({ state, dispatch }) => {
dispatch(state.tr.insertText(text));
return true;
}
};
}
Keyboard shortcuts
addKeyboardShortcuts() {
return {
'Mod-Shift-h': () => this.editor.commands.toggleHighlight()
};
}
ProseMirror plugins
Your extension can also define ProseMirror plugins which will let you perform more advanced things, such as listening to browser events attached to a node.
import { Plugin, PluginKey } from 'prosemirror-state';
// ...
addPmPlugins() {
return [
new Plugin({
key: new PluginKey('myPlugin'),
props: {
handleClickOn: (view, pos, node, nodePos, event, direct) => {
console.log("Node was clicked!");
}
},
})
];
}
You can read more about ProseMirror’s plugin system here.
Configuration
const ConfigurableExt = Extension.create({
addOptions() {
return {
color: '#0000FF',
enabled: true
};
},
addCommands() {
return {
applyColor: () => () => {
// Use this.options.color
}
};
}
});
// Configure when using
ConfigurableExt.configure({ color: '#FF0000' });
Type-safe factories
Use defineNode and defineMark instead of Node.create() / Mark.create() for typed attributes:
import { defineNode, defineMark } from 'superdoc/super-editor';
const CustomBlock = defineNode({
name: 'customBlock',
group: 'block',
content: 'inline*',
addAttributes() {
return {
level: { default: 1 },
collapsed: { default: false },
};
},
parseHTML() {
return [{ tag: 'div[data-custom]' }];
},
renderHTML({ HTMLAttributes }) {
return ['div', { 'data-custom': '' }, 0];
},
});
const CustomMark = defineMark({
name: 'customMark',
addAttributes() {
return {
color: { default: null },
};
},
});
With TypeScript generics you get full attribute autocompletion:
interface CustomBlockAttrs {
level: number;
collapsed: boolean;
}
const CustomBlock = defineNode<{}, {}, CustomBlockAttrs>({
name: 'customBlock',
// attrs are now typed as CustomBlockAttrs
});
Type guards
Use type guards when traversing the document to get typed node/mark attributes:
import { isNodeType, assertNodeType, isMarkType } from 'superdoc/super-editor';
// Type guard — narrows the type in if blocks
editor.state.doc.descendants((node) => {
if (isNodeType(node, 'paragraph')) {
// node.attrs is now typed as ParagraphAttrs
console.log(node.attrs.paragraphProperties?.styleId);
}
});
// Assertion — throws if type doesn't match
const node = state.doc.nodeAt(pos);
assertNodeType(node, 'table');
// node.attrs is now typed as TableAttrs
// Mark type guard
for (const mark of node.marks) {
if (isMarkType(mark, 'bold')) {
// mark.attrs is typed
}
}
Using your extension
new SuperDoc({
selector: '#editor',
document: 'document.docx',
editorExtensions: [
MyExtension,
]
});