Guides
Developer Guide
Advanced architectural patterns for Arkpad development.
Advanced Command Chaining
Arkpad's command chaining is State-Aware. Unlike traditional editors that batch commands, Arkpad applies each command to a "temporary state" before running the next.
editor.chain().focus("end").insertContent("Hello World").selectAll().toggleBold().run();The .selectAll() correctly perceives the new text because the state was updated internally after .insertContent().
Extension Storage API
Extensions maintain their own reactive data store separate from the document:
const CharacterCount = Extension.create({
name: "characterCount",
addStorage() {
return { characters: 0 };
},
onUpdate({ editor }) {
this.storage.characters = editor.getText().length;
},
});Accessing storage:
- Core:
editor.storage.characterCount.characters - React:
const chars = useEditorStorage(editor, 'characterCount', 'characters')
Global Attributes
Inject attributes into multiple node types at once:
addGlobalAttributes() {
return [{
types: ['paragraph', 'heading'],
attributes: {
align: {
default: 'left',
parseHTML: element => element.style.textAlign || 'left',
renderHTML: attributes => ({ style: `text-align: ${attributes.align}` })
}
}
}]
}React Integration
Global Context
Wrap your app in ArkpadProvider to access the editor anywhere:
function MyComponent() {
const { editor } = useArkpadContext();
return <button onClick={() => editor.commands.toggleBold()}>Bold</button>;
}Dynamic Schema Generation
Arkpad uses the SchemaBuilder to compile a schema at runtime based on provided extensions.
Benefits:
- Modular Nodes/Marks — Extensions inject their own schema
- Global Injection — Attributes are added during the build process
Cookbook: Common Patterns
Adding a Custom Class to Heading 4
addGlobalAttributes() {
return [{
types: ['heading'],
attributes: {
class: {
default: null,
renderHTML: (attributes) => {
if (attributes.level === 4) {
return { class: 'my-custom-h4-styling' }
}
return null
}
}
}
}]
}Implementing a Word Counter
const WordCounter = Extension.create({
name: "wordCounter",
addStorage() {
return { count: 0 };
},
onUpdate({ editor }) {
const text = editor.getText();
this.storage.count = text.split(/\s+/).filter((s) => s.length > 0).length;
},
});