skip to Main Content

I have a Next.js project with TypeScript. I take a reference from official document resulting to the followings.

BundledEditor.jsx:

// ** https://www.tiny.cloud/docs/tinymce/6/react-pm-bundle/ **//
import { Editor } from '@tinymce/tinymce-react'

// remove ssr, otherwise will cause ReferenceError: navigator is not defined

// TinyMCE so the global var exists
// eslint-disable-next-line no-unused-vars
import tinymce from 'tinymce/tinymce'

// DOM model
import 'tinymce/models/dom/model'

// Theme
import 'tinymce/themes/silver'

// Toolbar icons
import 'tinymce/icons/default'

// Editor styles
import 'tinymce/skins/ui/oxide/skin.min.css'

// importing the plugin js.
// if you use a plugin that is not listed here the editor will fail to load
import 'tinymce/plugins/advlist'
import 'tinymce/plugins/anchor'
import 'tinymce/plugins/autolink'
import 'tinymce/plugins/autoresize'
import 'tinymce/plugins/autosave'
import 'tinymce/plugins/charmap'
import 'tinymce/plugins/code'
import 'tinymce/plugins/codesample'
import 'tinymce/plugins/directionality'
import 'tinymce/plugins/emoticons'
import 'tinymce/plugins/fullscreen'
import 'tinymce/plugins/help'
import 'tinymce/plugins/image'
import 'tinymce/plugins/importcss'
import 'tinymce/plugins/insertdatetime'
import 'tinymce/plugins/link'
import 'tinymce/plugins/lists'
import 'tinymce/plugins/media'
import 'tinymce/plugins/nonbreaking'
import 'tinymce/plugins/pagebreak'
import 'tinymce/plugins/preview'
import 'tinymce/plugins/quickbars'
import 'tinymce/plugins/save'
import 'tinymce/plugins/searchreplace'
import 'tinymce/plugins/table'
import 'tinymce/plugins/template'
import 'tinymce/plugins/visualblocks'
import 'tinymce/plugins/visualchars'
import 'tinymce/plugins/wordcount'

// importing plugin resources
import 'tinymce/plugins/emoticons/js/emojis'

// Content styles, including inline UI like fake cursors
/* eslint import/no-webpack-loader-syntax: off */
// import contentCss from 'tinymce/skins/content/dark/content.css'

import contentCss from '!!raw-loader!tinymce/skins/content/default/content.min.css'
import contentUiCss from '!!raw-loader!tinymce/skins/ui/oxide/content.min.css'

export default function BundledEditor(props) {
  const { init, ...rest } = props

  // note that skin and content_css is disabled to avoid the normal
  // loading process and is instead loaded as a string via content_style
  return (
    <Editor
      init={{
        ...init,
        skin: false,
        content_css: false,
        content_style: [contentCss, contentUiCss, init.content_style || ''].join('n')
      }}
      {...rest}
    />
  )
}

I just import it on other file like this:

import BundledEditor from './BundledEditor';

It works, but if refresh, the page will show this error:

ReferenceError: navigator is not defined

So I change the import way like this:

// remove ssr, otherwise will cause ReferenceError: navigator is not defined

const BundledEditor = dynamic(() => import('../BundledEditor'), {
  ssr: false
})

then fix the issue.

But when I try to type yarn build to check the project.

It shows error:

info  - Collecting page data ...ReferenceError: navigator is not defined
    at /Users/motogod19/4iDPS/Sphere/sphere-brand-dev/node_modules/tinymce/tinymce.js:961:23
    at Object.<anonymous> (/Users/motogod19/4iDPS/Sphere/sphere-brand-dev/node_modules/tinymce/tinymce.js:31525:3)
    at Module._compile (node:internal/modules/cjs/loader:1254:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
    at Module.load (node:internal/modules/cjs/loader:1117:32)
    at Module._load (node:internal/modules/cjs/loader:958:12)
    at Module.require (node:internal/modules/cjs/loader:1141:19)
    at require (node:internal/modules/cjs/helpers:110:18)
    at 3868 (/Users/motogod19/4iDPS/Sphere/sphere-brand-dev/.next/server/pages/apps/campaign/BundledEditor.js:28:33)
    at __webpack_require__ (/Users/motogod19/4iDPS/Sphere/sphere-brand-dev/.next/server/webpack-runtime.js:25:42)

> Build error occurred
Error: Failed to collect page data for /apps/campaign/BundledEditor

I’m not familiar with Next.js, is any dynamic import should I do ?

2

Answers


  1. navigator is not defined means that you are bundling the code for server side, which do not have the browser related APIs. You need to bundle this for the client side (that is, for the browser).

    The issue is because tinymce do not support ssr (as evident from https://github.com/tinymce/tinymce/issues/3709)


    Since you are using Next.js 13 and pages router, change your BundleEditor.jsx to below:

    // ** https://www.tiny.cloud/docs/tinymce/6/react-pm-bundle/ **//
    import React from 'react';
    
    import { Editor } from '@tinymce/tinymce-react';
    
    if (typeof window !== 'undefined') {
        require('tinymce/tinymce');
        require('tinymce/models/dom/model');
        require('tinymce/themes/silver');
        require('tinymce/icons/default');
        require('tinymce/plugins/advlist');
        require('tinymce/plugins/anchor');
        require('tinymce/plugins/autolink');
        require('tinymce/plugins/autoresize');
        require('tinymce/plugins/autosave');
        require('tinymce/plugins/charmap');
        require('tinymce/plugins/code');
        require('tinymce/plugins/codesample');
        require('tinymce/plugins/directionality');
        require('tinymce/plugins/emoticons');
        require('tinymce/plugins/fullscreen');
        require('tinymce/plugins/help');
        require('tinymce/plugins/image');
        require('tinymce/plugins/importcss');
        require('tinymce/plugins/insertdatetime');
        require('tinymce/plugins/link');
        require('tinymce/plugins/lists');
        require('tinymce/plugins/media');
        require('tinymce/plugins/nonbreaking');
        require('tinymce/plugins/pagebreak');
        require('tinymce/plugins/preview');
        require('tinymce/plugins/quickbars');
        require('tinymce/plugins/save');
        require('tinymce/plugins/searchreplace');
        require('tinymce/plugins/table');
        require('tinymce/plugins/template');
        require('tinymce/plugins/visualblocks');
        require('tinymce/plugins/visualchars');
        require('tinymce/plugins/wordcount');
        require('tinymce/plugins/emoticons/js/emojis')
    }
    
    // Editor styles
    import 'tinymce/skins/ui/oxide/skin.min.css';
    
    // Content styles, including inline UI like fake cursors
    /* eslint import/no-webpack-loader-syntax: off */
    // import contentCss from 'tinymce/skins/content/dark/content.css'
    
    import contentCss from '!!raw-loader!tinymce/skins/content/default/content.min.css';
    import contentUiCss from '!!raw-loader!tinymce/skins/ui/oxide/content.min.css';
    
    export default function BundledEditor(props) {
        const { init, ...rest } = props;
    
        // note that skin and content_css is disabled to avoid the normal
        // loading process and is instead loaded as a string via content_style
        return (
            <Editor
                init={{
                    ...init,
                    skin: false,
                    content_css: false,
                    content_style: [contentCss, contentUiCss, init?.content_style || ''].join('n'),
                }}
                {...rest}
            />
        );
    }
    

    Both npm run dev and npm run build works fine.

    $ npm run build
    
    > [email protected] build
    > next build
    
    - info Linting and checking validity of types  
    - info Creating an optimized production build  
    - info Compiled successfully
    - info Collecting page data  
    - info Generating static pages (4/4)
    - info Finalizing page optimization  
    
    Route (pages)                              Size     First Load JS
    ┌ ○ /                                      3.39 kB        80.3 kB
    ├   └ css/00a586533d958f8e.css             1.73 kB
    ├   /_app                                  0 B            76.9 kB
    ├ ○ /404                                   182 B          77.1 kB
    ├ λ /api/hello                             0 B            76.9 kB
    └ ○ /BundledEditor                         473 kB          550 kB
        └ css/3191dae2698bb014.css             11.8 kB
    + First Load JS shared by all              77.6 kB
      ├ chunks/framework-63157d71ad419e09.js   45.2 kB
      ├ chunks/main-c6c319de9f7d0316.js        29.4 kB
      ├ chunks/pages/_app-5fbdfbcdfb555d2f.js  296 B
      ├ chunks/webpack-1f3c29ac3f0dceed.js     2.06 kB
      └ css/876d048b5dab7c28.css               706 B
    
    λ  (Server)  server-side renders at runtime (uses getInitialProps or getServerSideProps)
    ○  (Static)  automatically rendered as static HTML (uses no initial props)
    
    Login or Signup to reply.
  2. You are getting that error because the tinymce does not seem to be server-side rendering friendly, as executing browser specific code in the component body or at import. That has been said, using typeof window !== 'undefined' as suggested in the other answer is not recommended at all, as it will very likely cause a hydration error.

    Using dynamic import with ssr:false should work. In your case, it did not, because you have BundledEditor in pages directory, so it’s considered as a page for /BundledEditor causing another error.

    Just move BundledEditor outside pages, in a components folder for example, at the same level as pages, and import it like so:

    // pages/index.tsx
    
    import dynamic from "next/dynamic";
    
    const DynamicBundledEditor = dynamic(() => import("../../components/BundledEditor"), {
      ssr: false,
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search