# How to Add File Attachment Handling to Tiptap Editor: A Complete Guide

> Learn to add file attachment handling to your Tiptap editor with our guide. Utilize the built-in Attachment extension for streamlined uploads and styled nodes.

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

---

**The `reactjs-tiptap-editor` package provides a ready-made `Attachment` extension that handles file uploads via a configurable `upload` function and renders styled attachment nodes with automatic file type icons and size formatting.**

The `hunghg255/reactjs-tiptap-editor` repository ships with a comprehensive file attachment system for Tiptap that supports both drag-and-drop interactions and toolbar-based file selection. This solution manages the entire lifecycle from upload progress indication to final rendering, utilizing React node views for real-time feedback and offering full internationalization support.

## Install the Attachment Extension

The Attachment extension is included in the main package but requires a subpath import. Install the library if you haven't already:

```bash
pnpm add reactjs-tiptap-editor

# or yarn add reactjs-tiptap-editor

# or npm install reactjs-tiptap-editor

```

Import the extension from the dedicated attachment subpath to keep your bundle size optimized:

```tsx
import { Attachment } from 'reactjs-tiptap-editor/attachment';

```

## Configure the Upload Handler

At the core of the file attachment handling is the `upload` configuration option defined in [`src/extensions/Attachment/Attachment.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/Attachment/Attachment.ts). This async function receives a `File` object and must return the URL string where the file is stored.

```tsx
import { useEditor, EditorContent } from '@tiptap/react';
import { Attachment } from 'reactjs-tiptap-editor/attachment';

const editor = useEditor({
  extensions: [
    // ... other extensions
    Attachment.configure({
      upload: async (file: File) => {
        const formData = new FormData();
        formData.append('file', file);
        
        const response = await fetch('/api/upload', {
          method: 'POST',
          body: formData,
        });
        
        const data = await response.json();
        return data.url; // The URL stored in the node attribute
      },
    }),
  ],
});

```

The extension automatically invokes this handler when users drop files or select them via the file picker, inserting a node with these **attributes** managed by the `addAttributes()` method:

- `url` – The direct link to the uploaded asset
- `fileName` – Original name without extension
- `fileExt` – File extension (e.g., `pdf`, `docx`)
- `fileSize` – Size in bytes (formatted via `normalizeFileSize` from [`src/utils/file.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/utils/file.ts))
- `fileType` – MIME type used for icon selection
- `fileId` – Optional internal identifier for your application

## Add the Toolbar Button with RichTextAttachment

While the extension supports drag-and-drop, you typically need a toolbar button to trigger the file picker. The repository provides `RichTextAttachment` located at [`src/extensions/Attachment/components/RichTextAttachment.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/Attachment/components/RichTextAttachment.tsx).

```tsx
import { RichTextAttachment } from 'reactjs-tiptap-editor/attachment';

export const Toolbar = ({ editor }) => (
  <div className="toolbar">
    <RichTextAttachment editor={editor} />
  </div>
);

```

This component wraps the base `ActionButton` and executes `editor.chain().focus().setAttachment().run()` when clicked. The `setAttachment` command is registered by the main extension file and programmatically opens a hidden file input, handles the selection, and manages the upload flow.

## Understanding Node Rendering and React Views

The attachment system uses a hybrid rendering approach. When content is saved, the `renderHTML` method in [`Attachment.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/Attachment.ts) generates semantic markup:

```html
<div class="attachment">
  <a href="FILE_URL">
    <span class="attachment__icon">[icon]</span>
    <span class="attachment__text">FILE_NAME.EXT (SIZE)</span>
  </a>
</div>

```

During editing, the **React node view** in [`src/extensions/Attachment/components/NodeViewAttachment/NodeViewAttachment.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/Attachment/components/NodeViewAttachment/NodeViewAttachment.tsx) takes over. This component displays:

- **Uploading state**: A placeholder showing localized text from `editor.attachment.uploading`
- **File icons**: Determined by `getFileTypeIcon(fileType, true)` in [`FileIcon.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/FileIcon.tsx)
- **Size formatting**: Human-readable sizes via the `normalizeFileSize` utility

If the upload fails or is pending, users see the `editor.attachment.please_upload` message until the promise resolves.

## Customize Internationalization

All user-facing strings are externalized to locale files under `src/locales/*.ts`. The default English keys include:

- `editor.attachment.tooltip` – Toolbar button hover text
- `editor.attachment.uploading` – Displayed during file transfer
- `editor.attachment.please_upload` – Instruction in the placeholder state

Override these by extending the locale configuration when initializing your editor instance.

## Complete Working Example

Here is a minimal, production-ready setup demonstrating file attachment handling in a Tiptap editor:

```tsx
import { useEditor, EditorContent } from '@tiptap/react';
import { StarterKit } from '@tiptap/starter-kit';
import { Attachment, RichTextAttachment } from 'reactjs-tiptap-editor/attachment';

export default function App() {
  const editor = useEditor({
    extensions: [
      StarterKit,
      Attachment.configure({
        upload: async (file) => {
          // Replace with your actual upload logic
          const formData = new FormData();
          formData.append('file', file);
          const res = await fetch('/api/upload', { method: 'POST', body: formData });
          const { url } = await res.json();
          return url;
        },
      }),
    ],
    content: '<p>Drop files here or use the button below.</p>',
  });

  if (!editor) return null;

  return (
    <div className="editor-wrapper">
      <div className="toolbar">
        <RichTextAttachment editor={editor} />
      </div>
      <EditorContent editor={editor} />
    </div>
  );
}

```

When users click the attachment button or drop files into the editor, the configured upload handler processes the file, and the node view renders a styled preview with the correct icon and formatted file size.

## Summary

- **Import path**: Use `reactjs-tiptap-editor/attachment` for the `Attachment` extension and `RichTextAttachment` component
- **Core file**: [`src/extensions/Attachment/Attachment.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/Attachment/Attachment.ts) defines the node schema, `setAttachment` command, and HTML rendering
- **Upload requirement**: Provide an async `upload` function that returns a file URL string
- **React integration**: [`NodeViewAttachment.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/NodeViewAttachment.tsx) handles the editing UI with upload progress indicators
- **Toolbar button**: [`RichTextAttachment.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/RichTextAttachment.tsx) provides a ready-made button that triggers the attachment flow
- **Attributes**: The system tracks `url`, `fileName`, `fileExt`, `fileSize`, `fileType`, and optional `fileId` for each attachment

## Frequently Asked Questions

### How do I handle file uploads to my own API endpoint?

Configure the `upload` option when setting up the `Attachment` extension. This async function receives the `File` object and must return the hosted URL as a string. The extension handles the file input, calls your function, and inserts the node only after the promise resolves.

### Can I customize the appearance of attachment nodes in the editor?

Yes. The React node view in [`src/extensions/Attachment/components/NodeViewAttachment/NodeViewAttachment.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/Attachment/components/NodeViewAttachment/NodeViewAttachment.tsx) controls the editing appearance. You can fork this component or wrap it with your own styling. For saved content, override the CSS classes `attachment`, `attachment__icon`, and `attachment__text` that are generated by the `renderHTML` method.

### What file metadata does the extension store?

According to the `addAttributes()` method in [`Attachment.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/Attachment.ts), the extension persists six fields: `url` (string), `fileName` (string), `fileExt` (string), `fileSize` (number), `fileType` (string), and `fileId` (string, optional). The `fileSize` value is processed through `normalizeFileSize` in [`src/utils/file.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/utils/file.ts) for human-readable display.

### How do I change the text displayed during file uploads?

Modify the locale files in `src/locales/` or override the specific keys in your editor configuration. The relevant keys are `editor.attachment.uploading` for the progress message and `editor.attachment.please_upload` for the placeholder instruction. These strings are consumed by the `NodeViewAttachment` component during the upload lifecycle.