DEV Community

Andrei Kniazev
Andrei Kniazev

Posted on • Edited on

4 2

Migration from tiptap v.1 to tiptap v.2

If you are working with tiptap v.1 and want to upgrade to tiptap v.2 here is the story of how I did it for my project LoreHub.

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.
  2. Insert into the description's history table.

basic structure

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.

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;
      }
}
Enter fullscreen mode Exit fullscreen mode

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:

// 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);
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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

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);
      }
    }
Enter fullscreen mode Exit fullscreen mode

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 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.

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)

nextjs tutorial video

Youtube Tutorial Series 📺

So you built a Next.js app, but you need a clear view of the entire operation flow to be able to identify performance bottlenecks before you launch. But how do you get started? Get the essentials on tracing for Next.js from @nikolovlazar in this video series 👀

Watch the Youtube series