DEV Community

mugichaba
mugichaba

Posted on

Wysiwyg editor with Nuxt.js

I implemented wysiwyg editor with a library named tiptap.

Image description

Although I am almost a beginner in Nuxt.js, it was really easy to create the component.

FYI, the sample code uses buefy though I skip about installing and the detail of it.

Add tiptap to a project

The official website shows how to add it.
In my case, using yarn.



$ yarn add @tiptap/vue-2 @tiptap/starter-kit


Enter fullscreen mode Exit fullscreen mode

Implement a wysiwyg component

Create Tiptap.vue and write the following code as a component.



<template>
  <div class="editor">
    <div v-if="editor" class="menu">
      <b-tooltip label="bold" type="is-dark">
        <b-button
          :class="{ 'is-active': editor.isActive('bold') }"
          icon-left="format-bold"
          type="is-text"
          size="small"
          @click="editor.chain().focus().toggleBold().run()"
        />
      </b-tooltip>
      <b-tooltip label="italic" type="is-dark">
        <b-button
          :class="{ 'is-active': editor.isActive('italic') }"
          icon-left="format-italic"
          type="is-text"
          size="small"
          @click="editor.chain().focus().toggleItalic().run()"
        />
      </b-tooltip>
      <b-tooltip label="strike" type="is-dark">
        <b-button
          :class="{ 'is-active': editor.isActive('strike') }"
          icon-left="format-strikethrough"
          type="is-text"
          size="small"
          @click="editor.chain().focus().toggleStrike().run()"
        />
      </b-tooltip>
      <b-tooltip label="code" type="is-dark">
        <b-button
          :class="{ 'is-active': editor.isActive('code') }"
          icon-left="code-tags"
          type="is-text"
          size="small"
          @click="editor.chain().focus().toggleCode().run()"
        />
      </b-tooltip>
      <b-tooltip label="paragraph" type="is-dark">
        <b-button
          :class="{ 'is-active': editor.isActive('paragraph') }"
          icon-left="format-paragraph"
          type="is-text"
          size="small"
          @click="editor.chain().focus().setParagraph().run()"
        />
      </b-tooltip>
      <b-tooltip label="header1" type="is-dark">
        <b-button
          :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
          icon-left="format-header-1"
          type="is-text"
          size="small"
          @click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
        />
      </b-tooltip>
      <b-tooltip label="header2" type="is-dark">
        <b-button
          :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
          icon-left="format-header-2"
          type="is-text"
          size="small"
          @click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
        />
      </b-tooltip>
      <b-tooltip label="header3" type="is-dark">
        <b-button
          :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
          icon-left="format-header-3"
          type="is-text"
          size="small"
          @click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
        />
      </b-tooltip>
      <b-tooltip label="bulleted list" type="is-dark">
        <b-button
          :class="{ 'is-active': editor.isActive('bulletList') }"
          icon-left="format-list-bulleted"
          type="is-text"
          size="small"
          @click="editor.chain().focus().toggleBulletList().run()"
        />
      </b-tooltip>
      <b-tooltip label="ordered list" type="is-dark">
        <b-button
          :class="{ 'is-active': editor.isActive('orderedList') }"
          icon-left="format-list-numbered"
          type="is-text"
          size="small"
          @click="editor.chain().focus().toggleOrderedList().run()"
        />
      </b-tooltip>
      <b-tooltip label="undo" type="is-dark">
        <b-button
          icon-left="undo"
          type="is-text"
          size="small"
          @click="editor.chain().focus().undo().run()"
        />
      </b-tooltip>
      <b-tooltip label="redo" type="is-dark">
        <b-button
          icon-left="redo"
          type="is-text"
          size="small"
          @click="editor.chain().focus().redo().run()"
        />
      </b-tooltip>
    </div>
    <div class="input-field">
      <editor-content :editor="editor" />
    </div>
  </div>
</template>

<script>
import { Editor, EditorContent } from '@tiptap/vue-2';
import StarterKit from '@tiptap/starter-kit';

export default {
  components: {
    EditorContent,
  },

  props: {
    value: {
      type: String,
      default: '',
    },
  },

  data() {
    return {
      editor: null,
    };
  },

  watch: {
    value(value) {
      // HTML
      const isSame = this.editor.getHTML() === value;

      // JSON
      // const isSame = JSON.stringify(this.editor.getJSON()) === JSON.stringify(value)

      if (isSame) {
        return;
      }

      this.editor.commands.setContent(value, false);
    },
  },

  mounted() {
    this.editor = new Editor({
      content: this.value,
      extensions: [StarterKit],
      onUpdate: () => {
        // HTML
        this.$emit('input', this.editor.getHTML());

        // JSON
        // this.$emit('input', this.editor.getJSON())
      },
    });
  },

  beforeDestroy() {
    this.editor.destroy();
  },
};
</script>

<style lang="scss" scoped>
.editor {
  border: 1px solid lightgray;
  border-radius: 5px;
  margin-top: 10px;
  margin-bottom: 10px;
}
.menu {
  border-bottom: 1px solid lightgray;
  padding: 10px;
}
.input-field {
  padding: 10px;
}
</style>


Enter fullscreen mode Exit fullscreen mode

The block below represents one of the menu button.
I use Material icons so you can find the icons like format-bold on the website.



<b-tooltip label="bold" type="is-dark">
 <b-button
   :class="{ 'is-active': editor.isActive('bold') }"
   icon-left="format-bold"
   type="is-text"
   size="small"
   @click="editor.chain().focus().toggleBold().run()"
 />
</b-tooltip>


Enter fullscreen mode Exit fullscreen mode

When you call the component on any other component, you should pass a value like this.



<tiptap value="something">


Enter fullscreen mode Exit fullscreen mode

something must belong to like data().
And when you modify the text on the wysiwyg editor, something property is going to be changed.

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

While many AI coding tools operate as simple command-response systems, Qodo Gen 1.0 represents the next generation: autonomous, multi-step problem-solving agents that work alongside you.

Read full post →

Top comments (0)

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post