Working with elements within blocks - the building blocks of content structure
Elements are the individual components that make up a block’s content. While blocks represent top-level content units (paragraphs, headings, etc.), elements define the internal structure and hierarchy within those blocks.
Blocks are top-level containers identified by unique IDs
Elements are Slate.js nodes that define the block’s internal structure
Each block contains an array of elements in its value property
packages/core/editor/src/editor/types.ts
type SlateElement<K extends string = string, T = any> = { id: string; type: K; // Element type (kebab-case) children: Descendant[]; props?: PluginElementProps<T>;};
Element types use kebab-case (“paragraph”, “heading-one”, “accordion-list-item”) while block types use PascalCase (“Paragraph”, “HeadingOne”, “Accordion”).
// Get single element by IDconst element = editor.getElement({ blockId: 'block-123', elementId: 'elem-456'});if (element) { console.log('Element type:', element.type); console.log('Element props:', element.props);}// Get element at specific pathconst elementAtPath = editor.getElement({ blockId: 'block-123', at: [0, 0, 1]});// Get all elements in a blockconst elements = editor.getElements({ blockId: 'block-123'});// Get elements of specific typeconst links = editor.getElements({ blockId: 'block-123', filter: (element) => element.type === 'link'});
// Get direct children of elementconst children = editor.getElementChildren({ blockId: 'block-123', elementId: 'elem-456'});children.forEach(child => { if ('text' in child) { console.log('Text node:', child.text); } else { console.log('Element node:', child.type); }});
// Check if element has no contentconst isEmpty = editor.isElementEmpty({ blockId: 'block-123', elementId: 'elem-456'});if (isEmpty) { console.log('Element is empty');}
type PluginElement<TKeys, T> = { render?: (props: PluginElementRenderProps) => JSX.Element; props?: PluginElementProps<T>; asRoot?: boolean; // Is this the root element? children?: TKeys[]; // Allowed child element types injectElementsFromPlugins?: string[]; // Allow elements from other plugins rootPlugin?: string; // Plugin this element belongs to placeholder?: string; // Placeholder text when empty};
Use injectElementsFromPlugins to allow elements from other plugins to be nested. For example, an accordion content area can accept paragraphs, headings, images, etc.
Elements are located using Slate paths - arrays of indices:
// Path examples:[0] // First element in block[0, 0] // First child of first element[0, 1, 2] // Third child of second child of first element// Get element at pathconst block = editor.getBlock({ id: blockId });const slate = editor.blockEditorsMap[blockId];const [element] = Editor.node(slate, [0, 1]);
Slate paths are relative to the block’s Slate editor instance, not the entire document. Each block maintains its own independent Slate editor.
// Good: Type-safe and validatedconst element = editor.y('paragraph', { children: [editor.y.text('Hello')]});// Bad: Manual construction can miss required propertiesconst element = { id: 'manual-id', type: 'paragraph', children: [{ text: 'Hello' }]};