# How to Create Collapsible Drawer/Accordion Blocks in ReactJS Tiptap Editor

> Learn how to build collapsible drawer and accordion blocks in ReactJS Tiptap Editor. Extend the Drawer node and add a toggle command for interactive content.

- Repository: [Hung Hoang/reactjs-tiptap-editor](https://github.com/hunghg255/reactjs-tiptap-editor)
- Tags: how-to-guide
- Published: 2026-03-03

---

**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](https://github.com/ahmed-taj/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`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/Drawer/Drawer.ts)** – Defines the `richTextDrawer` node schema, attributes (`src`, `alt`, `width`, `height`, `align`), and commands including `setDrawer` and `setAlignImageDrawer`.
- **[`src/extensions/Drawer/components/RichTextDrawer.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/Drawer/components/RichTextDrawer.tsx)** – Provides the UI button that opens the drawing modal and inserts a new drawer node via the `setDrawer` command.
- **[`src/extensions/Drawer/components/NodeViewDrawer/NodeViewDrawer.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/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`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/Drawer/Drawer.ts) and modify the `addAttributes` method to store the collapse state:

```ts
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`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/Drawer.ts) file, add a command that flips the boolean value:

```ts
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`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/Drawer/components/NodeViewDrawer/NodeViewDrawer.tsx) to read the attribute and render a toggle button:

```tsx
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:

```css
.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`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/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:

1. **Define a new node** with a `collapsed` attribute in a new extension file (e.g., [`src/extensions/Accordion/Accordion.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/Accordion/Accordion.ts)).
2. **Register a toggle command** using `updateAttributes` to flip the boolean.
3. **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-editor` is defined in [`Drawer.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/Drawer.ts) and rendered via [`NodeViewDrawer.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/NodeViewDrawer.tsx).
- Adding a `collapsed` boolean attribute to the node schema allows the editor to store accordion state in the document model.
- The `toggleDrawerCollapse` command uses `commands.updateAttributes` to toggle the state without destroying the node's data.
- React node views can call `updateAttributes` directly 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`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/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`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/RichTextBubbleDrawer.tsx) that appears in the bubble menu when the drawer is selected. Both approaches call `updateAttributes` to toggle the `collapsed` state.