> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/migration-guides/migrating-to-v12.md).

# Migrate to WebViewer 12.0

Prepare for WebViewer 12.0 by reviewing Vite and ES6 class migration requirements, legacy UI retirement, API removals, and changes across WebViewer, DOCX Editor, and Spreadsheet Editor.

This guide outlines the changes you should review before upgrading from an earlier version to WebViewer 12.0. Use it to identify breaking changes, removed APIs, migration requirements, and any updates needed to ensure a smooth transition.

## Vite and ES6 class migration

WebViewer 12.0 modernizes its JavaScript architecture by replacing Webpack with Vite as the build tool and targeting native ES6 (`es2022`). As part of this transition, several framework-provided base types are now implemented as true ES6 classes, including:

* [Annotations.MarkupAnnotation](https://sdk.apryse.com/api/web/Core.Annotations.MarkupAnnotation.html)
* [Annotations.SelectionModel](https://sdk.apryse.com/api/web/Core.Annotations.SelectionModel.html)
* [Annotations.ControlHandle](https://sdk.apryse.com/api/web/Core.Annotations.ControlHandle.html)
* [Tools.GenericAnnotationCreateTool](https://sdk.apryse.com/api/web/Core.Tools.GenericAnnotationCreateTool.html)

Custom tools and annotations that extend these types must be updated to use standard ES6 class inheritance patterns.

### Breaking impact

Legacy ES5 inheritance patterns are no longer supported. For example, constructor calls such as:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
Tools.GenericAnnotationCreateTool.call(this, ...)
```

{% endcode %}
{% endtab %}
{% endtabs %}

Will throw the following error:

{% tabs %}
{% tab title="Text" %}
{% code lineNumbers="true" %}

```
TypeError: Class constructor ... cannot be invoked without 'new'
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Required changes

When migrating custom tools and annotations:

* Use ES6 `class` syntax.
* Replace `Parent.call(this, ...)` with `super(...)`.
* Replace `Parent.prototype.method.call(this, ...)` with `super.method(...)`.

The following example shows how to migrate a custom tool from the legacy ES5 inheritance pattern to standard ES6 class syntax.

{% tabs %}
{% tab title="Before (ES5 inheritance)" %}
{% code lineNumbers="true" %}

```js
// Old
const TriangleCreateTool = function (documentViewer) {
  Tools.GenericAnnotationCreateTool.call(this, documentViewer, TriangleAnnotation);
};

TriangleCreateTool.prototype = new Tools.GenericAnnotationCreateTool();

TriangleCreateTool.prototype.mouseMove = function (e) {
  // ...
};
```

{% endcode %}
{% endtab %}

{% tab title="After (ES6 class syntax)" %}
{% code lineNumbers="true" %}

```js
// New
class TriangleCreateTool extends Tools.GenericAnnotationCreateTool {
  constructor(documentViewer) {
    super(documentViewer, TriangleAnnotation);
  }

  mouseMove(e) {
    super.mouseMove(e);
    // ...
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Alternative migration path

We recommend migrating custom tools and annotations to ES6 classes. If build constraints or legacy code requirements prevent this, `Reflect.construct` can be used as a temporary compatibility bridge in place of `.call(this)`.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
function TriangleCreateTool(documentViewer) {
  const base = Reflect.construct(
    Tools.GenericAnnotationCreateTool,
    [documentViewer, TriangleAnnotation],
    TriangleCreateTool
  );
  Object.assign(this, base);
}

TriangleCreateTool.prototype = Object.create(Tools.GenericAnnotationCreateTool.prototype);
TriangleCreateTool.prototype.constructor = TriangleCreateTool;

TriangleCreateTool.prototype.mouseMove = function (e) {
  Tools.GenericAnnotationCreateTool.prototype.mouseMove.call(this, e);
  // ...
};
```

{% endcode %}
{% endtab %}
{% endtabs %}

## WebViewer changes

### Legacy UI removal

The [Legacy UI](/web/ui-customization/legacy-ui/legacy-ui.md) is no longer included with WebViewer 12.0. Moving forward, WebViewer uses the [Modular UI](/web/ui-customization/modular-ui/getting-started.md) as the default supported user interface and customization framework. This consolidation provides a single customization path, reduces package size, and simplifies ongoing maintenance and upgrades.

If you're upgrading from WebViewer 11.0 or earlier, review the following migration guidance and update any Legacy UI implementations to their Module UI equivalents. Key changes include:

* The WebViewer constructor no longer supports `ui: 'legacy'`.
* Legacy UI-specific APIs, including `setHeaderItems`, have been removed.
* Modular UI is now the only supported UI and remains the default experience.

For more details, see [Migrating to V11 Modular UI](/web/migration-guides/migrating-to-v11/migrating-to-v11-modular-ui.md). For API changes, see [WebViewer API changes](#webviewer-apis).

### UI event listeners

In WebViewer 12.0, event listeners registered with `instance.UI.addEventListener()` no longer receive a `CustomEvent` object. Event payloads are now passed directly to the callback as positional arguments.

{% tabs %}
{% tab title="Previous behavior" %}
{% code lineNumbers="true" %}

```js
// Before
instance.UI.addEventListener(
  instance.UI.Events.VISIBILITY_CHANGED,
  (event) => {
    const { element, isVisible } = event.detail;
  }
);

```

{% endcode %}
{% endtab %}

{% tab title="New behavior" %}
{% code lineNumbers="true" %}

```js
// After
instance.UI.addEventListener(
  instance.UI.Events.VISIBILITY_CHANGED,
  (element, isVisible) => {
    // ...
  }
);
```

{% endcode %}
{% endtab %}
{% endtabs %}

If your event handlers access values using `event.detail`, update them to accept the event payload directly as callback arguments.

**Argument patterns by event**

The following events provide multiple positional arguments:

{% tabs %}
{% tab title="Text" %}
{% code lineNumbers="true" %}

```
visibilityChanged(element, isVisible)
annotationFilterChanged(types, authors, colors, statuses, checkRepliesForAuthorFilter)
panelResized(element, width)
thumbnailDropped(before, after, count)
activeDocumentViewerChanged(active, previous)
languageChanged(previous, next)
```

{% endcode %}
{% endtab %}
{% endtabs %}

The following events provide a single object or array argument:

{% tabs %}
{% tab title="Text" %}
{% code lineNumbers="true" %}

```
themeChanged(theme)
modularUIImported(components)
outlineBookmarksChanged(data)
userBookmarksChanged(bookmarks)
```

{% endcode %}
{% endtab %}
{% endtabs %}

The following events don't provide a payload:

{% tabs %}
{% tab title="Text" %}
{% code lineNumbers="true" %}

```
viewerLoaded()
fileDownloaded()
multiViewerReady()
```

{% endcode %}
{% endtab %}
{% endtabs %}

Refer to [UI.Events](https://sdk.apryse.com/api/web/UI.html#toc261__anchor) for the complete payload definition for each event.

### Window event payloads

UI events dispatched on `window` continue to use `CustomEvent` for backward compatibility. However, the structure of `event.detail` has changed to match the new event listener payload format.

{% tabs %}
{% tab title="Previous behavior" %}
{% code lineNumbers="true" %}

```js
// Before
window.addEventListener('tabDeleted', (event) => {
  const { src, options, id } = event.detail;
});

```

{% endcode %}
{% endtab %}

{% tab title="New behavior" %}
{% code lineNumbers="true" %}

```js
// After
window.addEventListener('tabDeleted', (event) => {
  const [src, options, id] = event.detail;
});

```

{% endcode %}
{% endtab %}
{% endtabs %}

Review any code that reads values from `event.detail` and update it to use the new payload structure for affected events.

**Updated event.detail formats**

The following events now expose payload values as arrays instead of objects:

{% tabs %}
{% tab title="Text" %}
{% code lineNumbers="true" %}

```
visibilityChanged
panelResized
thumbnailDropped
annotationFilterChanged
activeDocumentViewerChanged
languageChanged
compareAnnotationsLoaded
documentMerged
tabAdded
tabMoved
beforeTabChanged
afterTabChanged
beforeTabDeleted
tabDeleted
```

{% endcode %}
{% endtab %}
{% endtabs %}

The following events now return a single value instead of an object:

{% tabs %}
{% tab title="Text" %}
{% code lineNumbers="true" %}

```
fullscreenModeToggled
  { isInFullscreen } → boolean

modularUIImported
  { importedComponents } → component value
```

{% endcode %}
{% endtab %}
{% endtabs %}

The following events now return a single-element array:

{% tabs %}
{% tab title="Text" %}
{% code lineNumbers="true" %}

```
userBookmarksChanged
selectedThumbnailChanged
```

{% endcode %}
{% endtab %}
{% endtabs %}

The payload format for the following events has not changed:

{% tabs %}
{% tab title="Text" %}
{% code lineNumbers="true" %}

```
themeChanged
outlineBookmarksChanged
viewerLoaded
fileDownloaded
multiViewerReady
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Embedded JavaScript

WebViewer has strengthened the security boundaries of the embedded JavaScript runtime for PDF documents. Embedded JavaScript is no longer executed through `eval` and now runs in a more restricted execution environment.

Embedded JavaScript that relies on any of the following may no longer work as expected:

* Dynamic code execution.
* Direct access to runtime or browser environment objects.
* Unsupported JavaScript language features.

Review and test any embedded JavaScript used in your PDF documents to ensure compatibility with the updated runtime.

## Breaking API changes

Replacement APIs may be located in a different namespace or class than the removed API. Review the replacement column carefully when updating your implementation.

### WebViewer APIs

The following WebViewer APIs were deprecated in previous releases and have been removed in v12.0.

**Core.AccessibleReadingOrderManager**

| **Removed API**              | **Replacement**                                                                                      |
| ---------------------------- | ---------------------------------------------------------------------------------------------------- |
| getPreProcessingLevel()      | —                                                                                                    |
| setPreProcessingLevel(level) | [Core.setPreRenderLevel(level)](https://sdk.apryse.com/api/web/Core.html#.setPreRenderLevel__anchor) |

**Core.Annotations**

| **Removed API**                               | **Replacement**                                                                                                                                                                                  |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| FreeTextAnnotation.disableEnterKeypress()     | [Core.Annotations.FreeTextAnnotation.RichTextEditor.disableEnterKeypress()](https://sdk.apryse.com/api/web/Core.Annotations.FreeTextAnnotation.RichTextEditor.html#disableEnterKeypress__anchor) |
| FreeTextAnnotation.enableEnterKeypress()      | [Core.Annotations.FreeTextAnnotation.RichTextEditor.enableEnterKeypress()](https://sdk.apryse.com/api/web/Core.Annotations.FreeTextAnnotation.RichTextEditor.html#enableEnterKeypress__anchor)   |
| FreeTextAnnotation.getEditor()                | [Core.EditBoxManager.getEditor(annotation)](https://sdk.apryse.com/api/web/Core.EditBoxManager.html#getEditor__anchor)                                                                           |
| Model3DAnnotation                             | —                                                                                                                                                                                                |
| SignatureWidgetAnnotation.isSignedDigitally() | [Core.Annotations.SignatureWidgetAnnotation.isSignedByAppearance()](https://sdk.apryse.com/api/web/Core.Annotations.SignatureWidgetAnnotation.html#isSignedByAppearance__anchor)                 |

{% hint style="info" %}
**Info**

3D annotations are no longer supported in WebViewer 12.0.
{% endhint %}

**Core.DocumentViewer**

| **Removed API**       | **Replacement**                                                                                 |
| --------------------- | ----------------------------------------------------------------------------------------------- |
| disableReadOnlyMode() | [UI.disableViewOnlyMode()](https://sdk.apryse.com/api/web/UI.html#.disableViewOnlyMode__anchor) |
| enableReadOnlyMode()  | [UI.enableViewOnlyMode()](https://sdk.apryse.com/api/web/UI.html#.enableViewOnlyMode__anchor)   |

**Core.Tools**

| **Removed API**             | **Replacement**                                                                                                                    |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Tool.disableTextSelection() | [Core.Tools.TextTool.disableTextSelection()](https://sdk.apryse.com/api/web/Core.Tools.TextTool.html#disableTextSelection__anchor) |
| Tool.enableTextSelection()  | [Core.Tools.TextTool.enableTextSelection()](https://sdk.apryse.com/api/web/Core.Tools.TextTool.html#enableTextSelection__anchor)   |

**UI**

| **Removed API**                  | **Replacement**                                                                                      |
| -------------------------------- | ---------------------------------------------------------------------------------------------------- |
| createToolbarGroup(toolbarGroup) | Use [Modular UI](/web/migration-guides/migrating-to-v11/migrating-to-v11-modular-ui.md) replacement. |
| disableHighContrastMode()        | —                                                                                                    |
| enableHighContrastMode()         | —                                                                                                    |
| setHeaderItems(headerCallback)   | Use [Modular UI](/web/migration-guides/migrating-to-v11/migrating-to-v11-modular-ui.md) replacement. |

{% hint style="info" %}
**Accessibility update**

High-contrast mode APIs have been removed. WebViewer's default UI now meets WCAG 2.2 AA requirements without additional configuration.
{% endhint %}

### DOCX Editor APIs

The following DOCX Editor APIs were deprecated in previous versions and have been removed in v12.0.

**Core.Document**

| **Removed API**            | **Replacement**                                                                                                                     |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| OfficeEditorCursorStyle    | [Core.Document.OfficeEditor.SelectionStyle](https://sdk.apryse.com/api/web/Core.Document.OfficeEditor.html#.SelectionStyle__anchor) |
| OfficeEditorParagraphStyle | [Core.Document.OfficeEditor.SelectionStyle](https://sdk.apryse.com/api/web/Core.Document.OfficeEditor.html#.SelectionStyle__anchor) |
| OfficeEditorSelectionStyle | [Core.Document.OfficeEditor.SelectionStyle](https://sdk.apryse.com/api/web/Core.Document.OfficeEditor.html#.SelectionStyle__anchor) |

**Core.Document.OfficeEditor**

| **Removed API**               | **Replacement**                                                                                                                                                                                        |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| acceptTrackedChange(id)       | [Core.Document.OfficeEditor.TrackedChangeManager.acceptTrackedChange(id)](https://sdk.apryse.com/api/web/Core.Document.OfficeEditor.TrackedChangeManager.html#acceptTrackedChange__anchor)             |
| getTrackChangeManager()       | [Core.Document.OfficeEditor.getTrackedChangeManager()](https://sdk.apryse.com/api/web/Core.Document.OfficeEditor.html#getTrackedChangeManager__anchor)                                                 |
| getTrackedChangeOOXMLIds()    | [Core.Document.OfficeEditor.TrackedChangeManager.getTrackedChangeOOXMLIds()](https://sdk.apryse.com/api/web/Core.Document.OfficeEditor.TrackedChangeManager.html#getTrackedChangeOOXMLIds__anchor)     |
| getTrackedChanges()           | [Core.Document.OfficeEditor.TrackedChangeManager.getTrackedChanges()](https://sdk.apryse.com/api/web/Core.Document.OfficeEditor.TrackedChangeManager.html#getTrackedChanges__anchor)                   |
| moveCursorToTrackedChange(id) | [Core.Document.OfficeEditor.TrackedChangeManager.moveCursorToTrackedChange(id)](https://sdk.apryse.com/api/web/Core.Document.OfficeEditor.TrackedChangeManager.html#moveCursorToTrackedChange__anchor) |
| rejectTrackedChange(id)       | [Core.Document.OfficeEditor.TrackedChangeManager.rejectTrackedChange(id)](https://sdk.apryse.com/api/web/Core.Document.OfficeEditor.TrackedChangeManager.html#rejectTrackedChange__anchor)             |
| TrackedChange                 | [Core.Document.OfficeEditor.TrackedChangeManager.TrackedChange](https://sdk.apryse.com/api/web/Core.Document.OfficeEditor.TrackedChangeManager.html#.TrackedChange__anchor)                            |

Undocumented Office Editor APIs also removed in v12.0:

* `Core.Document.OfficeEditor.toggleMainCursorStyle()`
* `Core.Document.OfficeEditor.freezeMainCursor()`
* `Core.Document.OfficeEditor.showMainCursor()`
* `Core.Document.OfficeEditor.getContentListType()`

### Spreadsheet Editor APIs

The following Spreadsheet Editor APIs have changed in v12.0. The `SpreadsheetEditorManager.setEditMode()` method is no longer asynchronous.

**Core.SpreadsheetEditorDocument**

| **Removed API**            | **Replacement**                                                                                                                                                                                              |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| getWorkbook()              | [Core.SpreadsheetEditor.SpreadsheetEditorManager.getWorkbook()](https://sdk.apryse.com/api/web/Core.SpreadsheetEditor.SpreadsheetEditorManager.html#getWorkbook__anchor)                                     |
| getClipboard()             | [Core.SpreadsheetEditor.SpreadsheetEditorManager.getSpreadsheetEditorClipboard()](https://sdk.apryse.com/api/web/Core.SpreadsheetEditor.SpreadsheetEditorManager.html#getSpreadsheetEditorClipboard__anchor) |
| selectCellRange(cellRange) | [Core.SpreadsheetEditor.SpreadsheetEditorManager.selectCellRange(cellRange)](https://sdk.apryse.com/api/web/Core.SpreadsheetEditor.SpreadsheetEditorManager.html#selectCellRange__anchor)                    |

###


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/migration-guides/migrating-to-v12.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
