Kia Kalista
Kia Kalista

Reputation: 411

error - ReferenceError: document is not defined NextJS

I'm getting an error message like this

error - ReferenceError: document is not defined

Why is this? I've never had an error like this so I'm really confused. Please help for the seniors there.

My code =

import { useState } from "react";
import dynamic from 'next/dynamic';
import { Quill } from "react-quill";
const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
import toolbarOptions from "./toolbar";

import 'react-quill/dist/quill.bubble.css';
const BubbleTheme = Quill.import("themes/bubble");

class ExtendBubbleTheme extends BubbleTheme {
  constructor(quill, options) {
    super(quill, options);

    quill.on("selection-change", (range) => {
      if (range) {
        quill.theme.tooltip.show();
        quill.theme.tooltip.position(quill.getBounds(range));
      }
    });
  }
}

Quill.register("themes/bubble", ExtendBubbleTheme);

import styles from '../styles/Home.module.css'

export default function Home() {
  return (
    <div className={styles.container}>
      <h1>Quill Editor</h1>
        <ReactQuill
          theme="bubble"
          placeholder="Compose an epic..."
          modules={{ toolbar: toolbarOptions }}
        />
    </div>
  )
}

Upvotes: 9

Views: 11184

Answers (6)

Naclin
Naclin

Reputation: 153

If you need to use Quill from "quill" set it in a seperate component and dynamiclly import that instead. Can be for using multiple editors, etc...

'use client';

import React, { useEffect, useRef } from "react";

import Quill from "quill";

export default function SomeComponent({}: PageProps) {
    const toolbarRef = useRef<HTMLDivElement>(null);
    const editorRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
        const createInstance = () => {
            const quillEditor = editorRef.current;
            const quillToolbar = toolbarRef.current;
            if (quillEditor && quillToolbar) {
                editorInstanceRef.current = new Quill(quillEditor, {
                    theme: "snow",
                    modules: { toolbar: quillToolbar },
                });
            }
        };
        createInstance();

    }, []);
    
    
   return (
     <div>
         <div ref={toolbarRef}>
            {/* Your toolbar... */}
         </div>
         <div ref={editorInstanceRef}></div>
     </div>
   )
}


'use client';

import dynamic from 'next/dynamic'
import React, { useMemo } from 'react'

export default function Page() {

    const DynamicTextEditor = useMemo(() => {

        return dynamic(() => import("@/components/SomeComponent"), {
        
        loading: () => <p>loading...</p>,
        
        ssr: false,
        
        });
        
    }, []);
        

  return (
    <div>
        <DynamicTextEditor />
    </div>
  )
}

Upvotes: 0

ali orooji
ali orooji

Reputation: 41

I used next/dynamic, and it worked fine. you can use this outside the component. like this:

const Quill = dynamic(() => import('react-quill'), { ssr: false })

And then use this in your component:

const YourComponent = () => {
    const [value, setValue] = useState("");
    return (
        <div>
            {/*... */}
            <ReactQuill theme="snow" value={value} onChange={setValue} />
        </div>
   );
};

You can also use next/dynamic inside your component. you need to memoize the variable in this case.

In both cases it's impotant to use ssr: false in options.

Upvotes: 0

Kaushal Sachan
Kaushal Sachan

Reputation: 1243

[Solved] Working solution...

Create Own Component file MyEditor.js and paste below code

import dynamic from 'next/dynamic';
import { useRef } from 'react';
import { useEffect, useState } from 'react';

const QuillNoSSRWrapper = dynamic(
  async () => {
    const { default: RQ } = await import('react-quill');
    return ({ ...props }) => <RQ {...props} />;
  },
  {
    ssr: false,
  }
);

const modules = {
  toolbar: [
    [{ header: '1' }, { header: '2' }, { header: '3' }, { font: [] }],
    [{ size: [] }],
    ['bold', 'italic', 'underline', 'strike', 'blockquote'],
    [
      { list: 'ordered' },
      { list: 'bullet' },
      { indent: '-1' },
      { indent: '+1' },
    ],
    ['link', 'image', 'video'],
    ['clean'],
  ],
  clipboard: {
    // toggle to add extra line breaks when pasting HTML:
    matchVisual: false,
  },
}


export default function MyEditor(props) {
  const qRef = useRef(null);


  const [content, setContent] = useState(props.value || '');
  
  useEffect(() => {
    props.setValue(content);
  }, [content])

  return (
    typeof window !== 'undefined' ? <QuillNoSSRWrapper ref={qRef} modules={modules} value={content} onChange={(c) => setContent(c)} theme="snow" /> : null
  )
}

Use it as

<MyEditor value={data} setValue={setData} />

Upvotes: 3

Danazy
Danazy

Reputation: 49

The solution below fixed it for me. By creating your Editor as a component

import React,{useMemo} from 'react'

import dynamic from "next/dynamic";

type Props = {}

export default function SomePageThatNeedsATextEditor({}: Props) {

const DynamicTextEditor = useMemo(() => {

return dynamic(() => import("@/components/TextEditor"), {

loading: () => <p>loading...</p>,

ssr: false,

});

}, []);

return (

<main>

<DynamicTextEditor prop1={...} prop2={...} />

</main>

)

}

source:https://www.reddit.com/r/nextjs/comments/12hd4h0/how_to_add_react_quill_in_nextjs/

Upvotes: 1

Trinh Hieu
Trinh Hieu

Reputation: 805

Whenever you encounter the error document is not defined, make sure your JS code is running on client side by making sure that:

  • document is declared within react useEffect hook
  • or by disabling server side rendering by testing first whether document is defined using this way: if ( typeof window !== 'undefined') {run your document code }

and not on server side since document is only defined on client browser.

In next.js 13 this can be done by disabling server sire rendering built in function as below:

    import "quill/dist/quill.snow.css";
    import "react-quill/dist/quill.snow.css"; // Import quill styles
    import dynamic from "next/dynamic";
    
    const ReactQuill = dynamic(import("react-quill"), { ssr: false });
    
    export default function Test() {
      const [text, setText] = useState('');

        return (<div> 
                <div className="form-group">
                              <label htmlFor="address_field">Enter Description
                              </label>
                             
                              <ReactQuill
                                value={text}
                                onChange={(value) => setText(value)}
                                modules={{
                                  toolbar: [
                                    [{ header: [1, 2, false] }],
                                    ["bold", "italic", "underline", "strike"],
                                    ["link"],
                                    [{ align: [] }],
                                    ["blockquote", "code-block"],
                                    [{ list: "ordered" }, { list: "bullet" }],
                                    [{ script: "sub" }, { script: "super" }],
                                  ],
                                }}
                                formats={[
                                  "header",
                                  "bold",
                                  "italic",
                                  "underline",
                                  "strike",
                                  "link",
                                  "align",
                                  "blockquote",
                                  "code-block",
                                  "list",
                                  "bullet",
                                  "script",
                                ]}
                              />
                            </div>
</div>
)}

Upvotes: 0

Shalom Effiom
Shalom Effiom

Reputation: 706

Ran into this same issue. Using a Next Dynamic Import with this functional component implementation works like a charm:

import { useState, useMemo } from "react";
import dynamic from "next/dynamic";

const YourComponent = () => {
  const [value, setValue] = useState("");
  const ReactQuill = useMemo(() => dynamic(() => import('react-quill'), { ssr: false }),[]);

  return (
    <div>
      {/*... */}
      <ReactQuill theme="snow" value={value} onChange={setValue} />
    </div>
  );
};


export default YourComponent;

Upvotes: 43

Related Questions