Migration from tiptap v.1 to tiptap v.2
Tags: tiptap, vue, dotnet, javascript
Published: 2021-12-23 09:48:19

---
title: Migration from tiptap v.1 to tiptap v.2
published: true
description: If you are working with tiptap v.1 and want to upgrade to tiptap v.2 here is the story how I did it for my project LoreHub.
tags: #tiptap #vue #dotnet #javascript
//cover_image: https://direct_url_to_image.jpg
---

If you are working with [tiptap](https://tiptap.dev) v.1 and want to upgrade to tiptap v.2 here is the story of how I did it for my project [LoreHub](https://LoreHub.app). 

**LoreHub's stack**
Back - .net 6, ef 6, c#
Front - Vue.js 2, Veutify, Pinia 

## Initial setup
I have a Vue.js component that does a put request that will update the description on the server. It will do two things: 
1. Updates in the description's snapshot table.
1. Insert into the description's history table.

![basic structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f39ogwe1x15ldgwh2r7g.png)

Here are parts from the Vue component. As you can see I initialize the editor and on an update, it fires debounce function. Debounce function allows it to do requests only if the user will stop updating the content for 3 seconds. The debounce function is from lodash. 

``` javascript
import debounce from "lodash-es/debounce";

function initEditor () {
    this.editor = new Editor({
        extensions: [
          // extensions
        ],
        onUpdate: ({ getJSON }) => {
          this.content = getJSON();
          this.isSaving = true;
          this.updateDocumentDescriptionOnServerDebounce();
        },
    });
}

function updateDocumentDescriptionOnServerDebounce: debounce(async function () {
    await this.updateDocumentDescriptionOnServer();
}, 3000),

async updateDocumentDescriptionOnServer() {
    try {
        this.serverError = null;
        // pinia store action - put to WebApi
        await useDescriptionStore().updateDescription(
          this.settingId,
          this.type,
          this.forId,
          this.content
        );
        this.isSavingValue = false;
      } catch (e) {
        // some error handling
        this.serverError = e;
        this.isSavingValue = false;
      }
}
```

## Time to upgrade

First of all, I do everything that is mentioned in the official upgrade guide - https://tiptap.dev/overview/upgrade-guide

So it took some time, but then I face a problem that requires me to migrate standard extensions names. Imagine I have gigabytes of data in my database that contains JSONs and I need to iterate through all of this to rename extensions types. It is not an option and there should be a better way to do it.

How do I solve it? My idea was to create a migration function that will do it on the front end. But I don't want it to run every time the description is loaded. The solution will be to save the state in the database that the migration was performed and I don't want it to do it again.

I decided to change my WebAPI and database. I introduce a new column 'EditorVersion'. Because I have too many raws to update I set this field as nullable, without a default value.

Example entity framework migration:

``` csharp
// ef 6 migration
migrationBuilder.AddColumn<string>(
    name: "EditorVersion",
    table: "Descriptions_History",
    type: "nvarchar(max)",
    nullable: true);

migrationBuilder.AddColumn<string>(
    name: "EditorVersion",
    table: "Descriptions_Description",
    type: "nvarchar(max)",
    nullable: true);
```

After this I've created a v2 description.get action on the back that returns not just JSON, but JSON and editorVersion. 

```
Description {
    value	string
    nullable: true
    editorVersion	string
    nullable: true
}
```

If editorVersion is null it will run this migration on the front:

``` javascript
migrateExtensions(content) {
      for (const node of content) {
        // tiptap 2 migrate extensions type from v.1 to v2.
        // https://tiptap.dev/overview/upgrade-guide#new-names-for-most-extensions
        if (node.type === "bullet_list") node.type = "bulletList";
        if (node.type === "code_block") node.type = "codeBlock";
        if (node.type === "hard_break") node.type = "hardBreak";
        if (node.type === "horizontal_rule") node.type = "horizontalRule";
        if (node.type === "list_item") node.type = "listItem";
        if (node.type === "ordered_list") node.type = "orderedList";
        if (node.type === "table_cell") node.type = "tableCell";
        if (node.type === "table_header") node.type = "tableHeader";
        if (node.type === "table_row") node.type = "tableRow";
        if (node.type === "todo_list") node.type = "taskList";
        if (node.type === "todo_item") node.type = "todo_item";

        // recursion
        if (node.content && node.content.length > 0)
          migrateExtensions(node.content);
      }
    }
```

After the migration is done it will perform put request that will send updated JSON and set editor version to 'tiptap_v2'.

## Conclusion

It took me about three days of work to migrate from tiptap v1 to tiptap v2. It includes migration for a [custom extension](https://github.com/worldpwn/tiptap-custom-link-vue-router) that uses a Vue router for links. The tiptap's team did a good job with the migration guide, thank you. It was straightforward and easy to do.

Overall I like the new tiptap's API and this little hack allows to do a lazy migration 😊.

I hope this guide will help you do the migration and if you have any questions feel free to ask.