How to Create Collapsible Drawer/Accordion Blocks in ReactJS Tiptap Editor
You can create collapsible drawer or accordion blocks in the reactjs-tiptap-editor by extending the existing Drawer node with a boolean collapsed attribute, adding a toggleDrawerCollapse command, and rendering a toggle button inside the React node view that calls updateAttributes to flip the state.
The hunghg255/reactjs-tiptap-editor repository ships with a Drawer block powered by easydrawer that renders SVG vector drawings. By default, this block is always visible. To add accordion-like collapse behavior, you extend the node's schema, register a toggle command, and modify the node view component to conditionally render content based on the collapsed attribute.
Understanding the Drawer Block Architecture
The Drawer functionality is implemented across three core files that you will extend:
src/extensions/Drawer/Drawer.ts– Defines therichTextDrawernode schema, attributes (src,alt,width,height,align), and commands includingsetDrawerandsetAlignImageDrawer.src/extensions/Drawer/components/RichTextDrawer.tsx– Provides the UI button that opens the drawing modal and inserts a new drawer node via thesetDrawercommand.src/extensions/Drawer/components/NodeViewDrawer/NodeViewDrawer.tsx– The React node view that renders the<img>element, handles selection, and manages resize handles.
Because the editor uses React-based node views, you can embed interactive controls directly inside the NodeViewWrapper to manipulate node attributes.
Adding Collapsible Behavior to the Drawer Block
Step 1: Extend the Node Schema with a collapsed Attribute
Open src/extensions/Drawer/Drawer.ts and modify the addAttributes method to store the collapse state:
addAttributes() {
return {
...this.parent?.(),
// existing attributes ...
collapsed: {
default: false,
parseHTML: element => element.getAttribute('data-collapsed') === 'true',
renderHTML: attributes => ({
'data-collapsed': attributes.collapsed,
}),
},
};
}
This attribute serializes to data-collapsed in the HTML output, ensuring the state persists when saving or exporting content.
Step 2: Create the toggleDrawerCollapse Command
Inside the same Drawer.ts file, add a command that flips the boolean value:
addCommands() {
return {
// ... existing commands
toggleDrawerCollapse:
() => ({ commands, state }) => {
const { from } = state.selection;
const node = state.doc.nodeAt(from);
if (!node || node.type.name !== this.name) return false;
return commands.updateAttributes(this.name, {
collapsed: !node.attrs.collapsed,
});
},
};
}
The command uses commands.updateAttributes to modify the node at the current selection without re-creating the node, preserving the SVG data and dimensions.
Step 3: Update the React Node View Component
Modify src/extensions/Drawer/components/NodeViewDrawer/NodeViewDrawer.tsx to read the attribute and render a toggle button:
export function NodeViewDrawer({ editor, node, updateAttributes, getPos, selected }: any) {
const { collapsed, align } = node?.attrs;
const toggleCollapse = () => {
updateAttributes({ collapsed: !collapsed });
};
return (
<NodeViewWrapper
className={`image-view ${collapsed ? 'collapsed' : ''}`}
style={{ width: '100%', textAlign: align }}
>
<button
type="button"
className="collapse-toggle"
onClick={toggleCollapse}
>
{collapsed ? 'Show drawer' : 'Hide drawer'}
</button>
{!collapsed && (
<div
data-drag-handle
draggable="true"
className={`image-view__body ${selected ? 'image-view__body--focused' : ''}`}
>
{/* Existing image and resize handlers */}
<img src={node.attrs.src} alt={node.attrs.alt} />
{/* ... resize UI ... */}
</div>
)}
</NodeViewWrapper>
);
}
The component conditionally renders the image container only when collapsed is false, while the toggle button remains visible to restore the content.
Step 4: Style the Collapsed State
Add minimal CSS to animate or hide the collapsed content:
.image-view.collapsed .image-view__body {
display: none;
}
.collapse-toggle {
margin-bottom: 0.5rem;
background: transparent;
border: none;
cursor: pointer;
color: var(--primary);
}
You can also integrate the toggle action into the bubble menu at src/components/Bubble/RichTextBubbleDrawer.tsx by calling editor?.commands.toggleDrawerCollapse() when the drawer node is selected.
Creating a Generic Accordion Extension
The same architectural pattern applies to any block type. To build a plain accordion without drawing capabilities:
- Define a new node with a
collapsedattribute in a new extension file (e.g.,src/extensions/Accordion/Accordion.ts). - Register a toggle command using
updateAttributesto flip the boolean. - Render a
<details>or custom wrapper in your React node view that checks the attribute to show or hide child content.
Because Tiptap treats every block as a ProseMirror node, you can wrap existing content types or create container nodes that manage collapse state independently of their children.
Summary
- The Drawer block in
hunghg255/reactjs-tiptap-editoris defined inDrawer.tsand rendered viaNodeViewDrawer.tsx. - Adding a
collapsedboolean attribute to the node schema allows the editor to store accordion state in the document model. - The
toggleDrawerCollapsecommand usescommands.updateAttributesto toggle the state without destroying the node's data. - React node views can call
updateAttributesdirectly from UI buttons to create interactive collapse toggles. - This pattern is reusable for any custom block type requiring accordion or drawer behavior.
Frequently Asked Questions
Does the reactjs-tiptap-editor include built-in accordion functionality?
No, the editor ships with a static Drawer block for vector drawings. The collapsible behavior must be added by extending the node schema with a collapsed attribute and implementing a toggle command, as described in the steps above.
How do I persist the collapsed state when saving content?
The renderHTML method in Drawer.ts serializes the collapsed attribute to data-collapsed on the DOM element. When you export the editor content via editor.getHTML(), the attribute is included in the markup. When loading content back into the editor, parseHTML restores the boolean value from the attribute.
Can I apply this pattern to other block types like tables or code blocks?
Yes. Any Tiptap node can be extended with custom attributes and React node views. You would follow the same three-step process: add the attribute to the schema, create a toggle command, and modify the node view component to conditionally render based on the attribute value.
Where should I add the collapse toggle button in the UI?
You have two options: add the button directly inside the NodeViewDrawer component so it appears next to every drawer instance, or add a toolbar action in RichTextBubbleDrawer.tsx that appears in the bubble menu when the drawer is selected. Both approaches call updateAttributes to toggle the collapsed state.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →