Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use TinyMCE in Nuxt?

I want to add this script to my Nuxt code:

<script>
  tinymce.init({
    selector: "#mytextarea",
    plugins: "emoticons",
    toolbar: "emoticons",
    toolbar_location: "bottom",
    menubar: false
  });
</script>

I can just throw it into my component's template body (This script has to be inside <body> not <head>) like this:

enter image description here

and interestingly enough it works but there are two issues here:

  1. It looks ugly
  2. It's not dynamic. For example I can't bind selector dynamically to a prop or a data property! It has to be hardcoded.

So I was wondering if anybody knows how can I integrate such scripts into my Nuxt project properly?

like image 463
seyet Avatar asked Sep 06 '25 03:09

seyet


2 Answers

  1. I really really doubt this works. Vue always ignores any <script> tags in the templates with error message:

Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <script>, as they will not be parsed.

  1. There is no reason to put this into the template. It is just a regular JS code you can execute as part of the component lifecycle. Below is very simple component wrapper that integrates TinyMCE into Vue app.

However I do not recommend doing this by yourself and instead use official Vue wrapper (docs) - use v3 for Vue 2. Official integration handles all the edge cases like component activation/deactivation (when used together with <keep-alive>) and proper cleanup when the component is destroyed which is required in order to avoid nasty memory leaks

const ed = Vue.component("MyEditor", {
  props: ["id", "value"],
  template: `
    <div>
      <textarea :id="id" :value="value"></textarea>
    </div>
  `,
  mounted() {
    const me = this;
    window.tinymce.init({
      selector: "#" + this.id,
      plugins: "emoticons",
      toolbar: "emoticons",
      toolbar_location: "bottom",
      menubar: false,
      setup: function (editor) {
        editor.on("change input undo redo", function () {
          me.$emit('input', editor.getContent({format: 'text'}))
        });
      }
    });
  }
});

Demo

Nuxt

As TinyMCE is completely JS rendered, it does not make sense to execute it during server side rendering. In fact, above code will not work at all on server as there is no window variable. Use Nuxt's <client-only> component to force Nuxt to render editor only on the client...

UPDATE: On the other side the mounted lifecycle hook is not called during SSR at all so maybe this will work just fine even without the <client-only>

like image 193
Michal Levý Avatar answered Sep 07 '25 21:09

Michal Levý


As mentioned in the accepted answer, the official vue package is the way to go. Even with the official package, there were a number of issues once I pulled it into Nuxt.

Here's a step by step of how I got it working. Here are the versions of everything involved:

    "tinymce": "^5.10.3",
    "@tinymce/tinymce-vue": "^3.2.8",
    "vue": "^2.6.11",
    "nuxt": "2.15.8",

The tinymce-vue package requires a tinymce to be available. You can either install it via npm like above, or use the cloud-hosted version (see Prerequisites here).

Nuxt-specific Issues

You'll need to ensure the component is wrapped in the <client-only> tag to avoid SSR errors.

Additionally, if self-hosting the tinymce package, you'll want to only import it on the client-side. Otherwise you will see errors like: [Vue warn]: Failed to resolve async component: ... Reason: ReferenceError: navigator is not defined

You can do this by using require and if (process.client) { around them.

Ex:

if (process.client) {
  require('tinymce/tinymce')
  require('tinymce/themes/silver')
  require('tinymce/icons/default')
  require('tinymce/plugins/lists') // do this for any plugins you use on the editor
}

import Editor from '@tinymce/tinymce-vue'

At this point the editor should be loading on your page, but styles are probably not loading.

You can fix this by adding require('tinymce/skins/ui/oxide/skin.min.css') below the other requires.

Now the styles will be fixed but the tinymce theme will still look for other CSS files like mobile, min versions on its own, and cause network errors.

Ex:

404 http://localhost:3000/_nuxt/skins/ui/oxide/content.min.css
404 http://localhost:3000/_nuxt/skins/ui/oxide/skin.min.css

For content: You can either copy that file to the static folder with the same path, or override them with the content_css setting (in the Vue component init options).

For skin: Since you already provided it as a module, set skin: false in the Vue component init options.

UPDATED 2022-11-29: Fixed Typo

like image 39
Grey Vugrin Avatar answered Sep 07 '25 23:09

Grey Vugrin