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:
- Updates in the description's snapshot table.
- Insert into the description's history table.
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;
}
}
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);
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:
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 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.
Top comments (0)