Can't Build a Next 13 App with React-Quill

I have a NextJS app that uses react-quill as a rich text editor library.

Everything went fine importing the library dynamically avoiding SSR issues, but since I upgraded to Next 13 I don't manage to get it to work.

I get the following error in the build logs:

.../quill-test/node_modules/quill/dist/quill.js:7661
var elem = document.createElement('div');
           ^

ReferenceError: document is not defined

Here is my implementation of the RichTextEditor component, as you can see I tried a few of the things that are suggested in the issues you find on Github and elswhere:

import 'react-quill/dist/quill.snow.css';
import dynamic from 'next/dynamic';
import styles from './RichTextEditor.module.css';
import classnames from 'classnames';

// Importing this client side as it mounts on the document object
const ReactQuill = dynamic(() => import('react-quill'), { ssr: false });

const registerQuill = async () => {
  const { Quill } = await (await import('react-quill')).default;

  const Link = Quill.import('formats/link');
  // Override the existing property on the Quill global object and add custom protocols
  Link.PROTOCOL_WHITELIST = [
    'http',
    'https',
    'mailto',
    'tel',
    'radar',
    'rdar',
    'smb',
    'sms',
  ];

  class CustomLinkSanitizer extends Link {
    static sanitize(url: string) {
      // Run default sanitize method from Quill
      const sanitizedUrl = super.sanitize(url);

      // Not whitelisted URL based on protocol so, let's return `blank`
      if (!sanitizedUrl || sanitizedUrl === 'about:blank') return sanitizedUrl;

      // Verify if the URL already have a whitelisted protocol
      const hasWhitelistedProtocol = this.PROTOCOL_WHITELIST.some(
        (protocol: string) => {
          return sanitizedUrl.startsWith(protocol);
        }
      );

      if (hasWhitelistedProtocol) return sanitizedUrl;

      // if not, then append only 'http' to not to be a relative URL
      return `
    }
  }

  Quill.register(CustomLinkSanitizer, true);
};

registerQuill();

interface RichTextEditorProps {
  value: string;
  setValue: (value: string) => void;
  placeholder?: string;
  isError?: boolean;
}

const RichTextEditor = ({
  value,
  setValue,
  placeholder,
  isError,
}: RichTextEditorProps) => {
  return (
    <ReactQuill
      theme="snow"
      value={value}
      onChange={setValue}
      className={classnames(styles.container, isError && styles.error)}
      placeholder={placeholder}
    />
  );
};

export default RichTextEditor;

I reproduced it in an isolated project if you want to run it and see the full log.

Any help/advice appreciated!

M

3

2 Answers

turns out the badboy that was still running on the server was the registerQuill() call.

I solved moving it inside the component and only calling it client side after component mount.

...
const RichTextEditor = ({
  value,
  setValue,
  placeholder,
  isError,
}: RichTextEditorProps) => {
const [isMounted, setIsMounted] = useState(false)

useEffect(() => {
  setIsMounted(true)
  registerQuill()
}, [])

if (!isMounted) return null

  return (
    <ReactQuill
      theme="snow"
      value={value}
      onChange={setValue}
      className={classnames(styles.container, isError && styles.error)}
      placeholder={placeholder}
    />
  );
};
1

add this before the function

const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Elena Rostova

Elena Rostova

Lead Health, Wellness & Medical Journalist

Elena Rostova holds a Master's degree in Public Health Journalism. She covers groundbreaking medical research, holistic wellness trends, mental health awareness, and nutritional science.

Share this article
Twitter Facebook Pinterest