---
title: "Import .docx in your editor"
description: "Learn how to import DOCX (Word) documents into a Tiptap editor using the Import extension in our docs."
canonical_url: "https://tiptap.dev/docs/conversion/import/docx/editor-extension"
---

# Import .docx in your editor

Learn how to import DOCX (Word) documents into a Tiptap editor using the Import extension in our docs.

- **1. Activate trial or subscribe**

  Start a [free trial](https://cloud.tiptap.dev/v2?trial=true) or [subscribe to the Start
  plan](https://cloud.tiptap.dev/v2/billing) in your account.
- **2. Install from private registry**

  To install this frontend extensions, authenticate to Tiptap’s private npm registry by following
  the [setup guide](https://tiptap.dev/docs/guides/pro-extensions.md).

> **Interactive demo:** [ImportDocx](https://embed-pro.tiptap.dev/preview/Extensions/ImportDocx)

Converting `.docx` files to Tiptap JSON is simple with the `@tiptap-pro/extension-import-docx` editor extension, which integrates directly into your Tiptap editor.

You can also import via the [REST API](https://tiptap.dev/docs/conversion/import/docx/rest-api.md). Both paths use the same conversion service and produce identical content output. The main differences:

|                        | Editor extension                     | REST API                         |
| ---------------------- | ------------------------------------ | -------------------------------- |
| Runs in                | Your editor (client-side)            | Tiptap cloud (server-side)       |
| Footnotes and endnotes | Not surfaced in callback             | Returned in response             |
| Headers and footers    | Auto-applied via Pages extension     | Returned as separate data fields |
| Unknown content        | Rewritten against your editor schema | Raw JSON, no schema filtering    |

Choose the REST API if you need server-side processing, access to footnote or endnote data, or raw unfiltered JSON.

## Install the DOCX Import extension

The Conversion extensions are published in Tiptap’s private npm registry. Integrate the extensions by following the [private registry guide](https://tiptap.dev/docs/guides/pro-extensions.md).

Install the Tiptap Import extension package:

```bash
npm i @tiptap-pro/extension-import-docx
```

Ensure your editor includes all necessary Tiptap extensions to handle content from DOCX. For the complete list of extensions, configuration, and what each one handles, see the [ConvertKit](https://tiptap.dev/docs/conversion/import/docx/convertkit.md) page.

## Required extensions

Install [ConvertKit](https://tiptap.dev/docs/conversion/import/docx/convertkit.md) — it bundles every extension the importer needs: the standard nodes and marks, `TextStyleKit` for color/font/size/line-height/background, `TextAlign` preconfigured for `['paragraph', 'heading']`, `Highlight` with `multicolor: true`, an `Image` with DOCX crop attributes, `ConvertTableKit` for DOCX-aware tables, and `PageBreak`:

```ts
import { ConvertKit } from '@tiptap-pro/extension-convert-kit'

new Editor({ extensions: [ConvertKit] })
```

You can disable any individual slot via `ConvertKit.configure({ slot: false })` if you need to swap one of the bundled extensions for your own (e.g. when pairing with the [Pages extension](https://tiptap.dev/docs/pages/getting-started/overview.md), set `table: false` and register `TableKit` from `@tiptap-pro/extension-pages-tablekit` instead). See the [ConvertKit reference](https://tiptap.dev/docs/conversion/import/docx/convertkit.md) for the full slot list.

## Configure

Add the Import extension to your editor setup.

```ts
import { ImportDocx } from '@tiptap-pro/extension-import-docx'

const editor = new Editor({
  extensions: [
    // Other extensions ...
    ImportDocx.configure({
      appId: 'your-app-id', // Your Convert App ID (see Tiptap Cloud settings)
      token: 'your-jwt', // JWT for authentication (see Authentication documentation)
      imageUploadConfig: {
        url: 'https://your-image-upload-endpoint.com',
      },
    }),
    // Other extensions ...
  ],
  // Other editor settings ...
})
```

| Property            | Description                                                                                                                                                                                                                                                                      |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `appId`             | The ID of your Tiptap Convert app (find this in your Tiptap account's [conversion settings](https://cloud.tiptap.dev/convert-settings))                                                                                                                                          |
| `token`             | A JWT authentication token generated by your server for the Convert service. (See the [Authentication guide](https://cloud.tiptap.dev/convert-settings) for details on obtaining and using these credentials.)                                                                   |
| `imageUploadConfig` | Structured configuration for image uploads during import. See [Image upload configuration](#image-upload-configuration) for details.                                                                                                                                             |
| `verbose`           | A `string \| number` configuration property to help you control the level of diagnostic output during the import process. This is especially useful for debugging or for getting more insight into what happens during conversion. See more at [Verbose output](#verbose-output) |

## Image upload configuration

The `imageUploadConfig` option lets you configure how the conversion service uploads images found in the DOCX file to your server. This replaces the deprecated `imageUploadCallbackUrl` string option and gives you full control over headers, HTTP method, and query parameters.

```ts
ImportDocx.configure({
  appId: 'your-app-id',
  token: 'your-jwt',
  imageUploadConfig: {
    url: 'https://your-server.com/upload-image',
    headers: {
      Authorization: 'Bearer your-upload-token',
      'X-Custom-Header': 'custom-value',
    },
    method: 'PUT',
    queryParams: {
      bucket: 'images',
      folder: 'docx-imports',
    },
  },
})
```

| Property      | Type                     | Required | Description                                                                                                                                                                                                         |
| ------------- | ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `url`         | `string`                 | Yes      | The endpoint URL that will receive the uploaded images. Must be a valid HTTP or HTTPS URL.                                                                                                                          |
| `headers`     | `HeadersInit`            | No       | Additional HTTP headers forwarded by the convert service when uploading images. Useful for authentication (e.g., Bearer tokens, API keys). Accepts a plain object, `Headers` instance, or array of key-value pairs. |
| `method`      | `string`                 | No       | The HTTP method used to upload images. Defaults to the convert service's own default when omitted.                                                                                                                  |
| `queryParams` | `Record<string, string>` | No       | Query parameters appended to the upload URL by the convert service.                                                                                                                                                 |

> **Migrating from imageUploadCallbackUrl:**
>
> The `imageUploadCallbackUrl` option is deprecated. Replace it with `imageUploadConfig`:
>
> ```ts
> // Before (deprecated)
> ImportDocx.configure({
>   imageUploadCallbackUrl: 'https://your-server.com/upload-image',
> })
>
> // After
> ImportDocx.configure({
>   imageUploadConfig: {
>     url: 'https://your-server.com/upload-image',
>   },
> })
> ```
>
> If both `imageUploadConfig` and `imageUploadCallbackUrl` are provided, `imageUploadConfig` takes precedence and a console warning is emitted.

## Import a DOCX file

Once the extension is configured, you can import a DOCX file selected by the user.

### Basic import

The simplest approach is to pass the file directly to the `importDocx` command. Here it replaces the current editor content with the converted content and focuses the editor:

```ts
editor.chain().focus().importDocx({ file }).run()
```

In most cases, this one-liner is all you need to let users import `.docx` files. The extension handles sending the file to the conversion endpoint, retrieving the converted Tiptap JSON, and inserting it into the editor.

### Import handling

In order to have more control after the import process have finished, you would use the `onImport` callback to handle the conversion result. This callback provides the converted content, any errors that occurred, and a function called `setEditorContent` to insert the content from `context.content` into the editor. If you don't provide an `onImport` callback, the extension will automatically insert the content into the editor but you won't be able to handle anything else like errors or loading states.

```ts
editor
  .chain()
  .importDocx({
    file,
    onImport(context) {
      // Discriminated union: handle the failure branch first.
      if (context.error) {
        showErrorToast({ message: context.error.message })
        return
      }

      const { setEditorContent, content } = context

      // You could modify the content before inserting it.
      content.content?.push({
        type: 'paragraph',
        content: [{ type: 'text', text: 'Hello!' }],
      })

      // You can change the loading state of your application for example
      isLoading = false

      // Insert the (possibly modified) content. Pass it to the context-provided
      // `setEditorContent` function, which does the right wiring around it
      // (including auto-applying headers/footers when Pages is registered).
      setEditorContent(content)
    },
  })
  .focus()
  .run()
```

Operations that we have controlled in the example above:

| Operation            | Description                                                                                                                                                                                                                                                                         |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Error Handling       | If the conversion fails, you can display a toast or log the error.                                                                                                                                                                                                                  |
| Content Modification | You can insert extra nodes, remove certain nodes, or otherwise adjust the converted Tiptap JSON as needed.                                                                                                                                                                          |
| Editor Insertion     | If you want to rely on the extension's default insertion behavior (replacing the editor content), you can call the `setEditorContent()` function provided in the callback. If you modify the content yourself, you must manually set it with `editor.commands.setContent(content)`. |

## Headers & Footers

When importing a `.docx` file that contains headers and footers, the import extension automatically detects and applies them if the [Pages extension](https://tiptap.dev/docs/pages/getting-started/overview.md) is installed. Without Pages, the header/footer data is still returned to your `onImport` callback (see [Manual handling](#manual-handling) below) but has nowhere to render — install Pages to make them visible in the editor.

### Automatic handling

If the Pages extension is registered in your editor, headers and footers are applied automatically when you call `setEditorContent()`:

```ts
editor
  .chain()
  .importDocx({
    file,
    onImport(context) {
      if (context.error) {
        console.error(context.error)
        return
      }

      // Headers and footers are applied automatically alongside the body content
      context.setEditorContent()
    },
  })
  .focus()
  .run()
```

If the Pages extension is not installed, the header and footer data is still available in the `onImport` callback but is not automatically applied to the editor.

### Manual handling

The `onImport` callback provides all header and footer fields for manual processing:

```ts
editor
  .chain()
  .importDocx({
    file,
    onImport(context) {
      if (context.error) {
        console.error(context.error)
        return
      }

      // Access header/footer data directly
      const {
        header, // Default header (Tiptap JSON or null)
        footer, // Default footer (Tiptap JSON or null)
        headerFirstPage, // First page header (Tiptap JSON or null)
        footerFirstPage, // First page footer (Tiptap JSON or null)
        headerOdd, // Odd page header (Tiptap JSON or null)
        footerOdd, // Odd page footer (Tiptap JSON or null)
        headerEven, // Even page header (Tiptap JSON or null)
        footerEven, // Even page footer (Tiptap JSON or null)
      } = context

      // Set body content without automatic header/footer application
      editor.commands.setContent(context.content)

      // Manually apply headers and footers via Pages extension commands
      if (header) editor.commands.setHeader(header)
      if (footer) editor.commands.setFooter(footer)

      if (headerFirstPage || footerFirstPage) {
        editor.commands.setDifferentFirstPage(true)
        if (headerFirstPage) editor.commands.setHeaderFirstPage(headerFirstPage)
        if (footerFirstPage) editor.commands.setFooterFirstPage(footerFirstPage)
      }

      if (headerOdd || headerEven || footerOdd || footerEven) {
        editor.commands.setDifferentOddEven(true)
        if (headerOdd) editor.commands.setHeaderOdd(headerOdd)
        if (headerEven) editor.commands.setHeaderEven(headerEven)
        if (footerOdd) editor.commands.setFooterOdd(footerOdd)
        if (footerEven) editor.commands.setFooterEven(footerEven)
      }
    },
  })
  .focus()
  .run()
```

### Available fields

| Field             | Description                                                                        |
| ----------------- | ---------------------------------------------------------------------------------- |
| `header`          | Default header content as Tiptap JSON, or `null`                                   |
| `footer`          | Default footer content as Tiptap JSON, or `null`                                   |
| `headerFirstPage` | First page header (when "Different First Page" is enabled in Word), or `null`      |
| `footerFirstPage` | First page footer (when "Different First Page" is enabled in Word), or `null`      |
| `headerOdd`       | Odd page header (when "Different Odd & Even Pages" is enabled in Word), or `null`  |
| `footerOdd`       | Odd page footer (when "Different Odd & Even Pages" is enabled in Word), or `null`  |
| `headerEven`      | Even page header (when "Different Odd & Even Pages" is enabled in Word), or `null` |
| `footerEven`      | Even page footer (when "Different Odd & Even Pages" is enabled in Word), or `null` |

### Page-number fields

Word headers and footers commonly contain `PAGE` and `NUMPAGES` field codes that render as live page numbers. By default, when the [Pages extension](https://tiptap.dev/docs/pages/getting-started/overview.md) is installed, `importDocx` translates those fields into text tokens that match your active Pages registry — `{page}` and `{total}` out of the box — so they round-trip cleanly back to live Word fields on re-export with no extra config.

When you rename the built-ins, the importer forwards your registry to the convert service so it emits the same names:

```ts
import { Pages } from '@tiptap-pro/extension-pages'

Pages.configure({
  placeholders: { total: 'pages' },
})

// A Word `NUMPAGES` field now imports as `{pages}`, which the editor
// preview substitutes live and the export turns back into a `NUMPAGES`
// field.
```

This wiring is automatic — `importDocx` reads `Pages.options.placeholders` and forwards it to the convert service. You don't need to do anything in the `onImport` callback.

#### Disabling token translation

Pass `placeholders: false` on the import command to opt out. Word's cached numeric value flows through as plain text instead — the historical behavior, useful when no Pages extension is installed downstream:

```ts
editor.chain().importDocx({ file, placeholders: false }).run()
```

When `placeholders` is omitted, the command opts in by default whenever the Pages extension is installed — unless you've disabled token substitution with `Pages.configure({ placeholders: false })`, in which case it stays off. Pass `true` (or an explicit `{ page?, total? }` rename map) to force translation on regardless, with the rename map taking precedence over the editor's Pages config.

## Footnotes & Endnotes

> **Footnotes and endnotes are not available in the extension's onImport callback:**
>
> The REST API returns footnote and endnote data in its response (as `footnotes` and `endnotes`
> fields), but the editor extension does not currently surface these in the `onImport` callback's
> `ImportContext`. If you need footnote/endnote data, use the [REST
> API](https://tiptap.dev/docs/conversion/import/docx/rest-api.md) directly and extract the `footnotes` and `endnotes` fields
> from the response.

The import API can extract footnote and endnote content from DOCX files. Inline references in the document body are represented as `footnoteReference` and `endnoteReference` nodes, each with a `noteId` attribute. However, these nodes require a [custom extension](https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing.md) to render in the editor.

## Verbose output

The DOCX import extension provides a `verbose` configuration property to help you control the level of diagnostic output during the import process. This is especially useful for debugging or for getting more insight into what happens during conversion.

The `verbose` property is a bitmask number that determines which types of log messages are emitted. The extension uses the following levels:

| Value | Level   | Description                |
| ----- | ------- | -------------------------- |
| 1     | `log`   | General informational logs |
| 2     | `warn`  | Warnings                   |
| 4     | `error` | Errors                     |

> **Verbose bitmask:**
>
> You can combine levels by adding their values together. For example, `verbose: 3` will enable both
> `log` (1) and `warn` (2) messages.

The verbose output will give you, along the `data` property, one more property called `logs`, which will contain `info`, `warn`, and `error` properties, each of them being an array with all of the information related to that specific verbosity.

```js
{
  "data": {
    "content": {
        // Tiptap JSON
    }
  },
  "logs": {
    "info": [],
    "warn": [
      {
        "message": "Image file not found in media files",
        "fileName": "image1.gif",
        "availableMediaFiles": []
      }
    ],
    "error": [
      {
        "message": "Image upload failed: General error",
        "fileName": "image1.gif",
        "url": "https://your-image-upload-endpoint.com",
        "error": "Unable to connect. Is the computer able to access the url?",
        "context": "uploadImage general error"
      }
    ]
  }
}
```

## Support & Limitations

For a detailed breakdown of which document features are supported at each stage of the pipeline, see the [Supported features](https://tiptap.dev/docs/conversion/getting-started/feature-support-matrix.md) matrix.
