DEV Community

A7U
A7U

Posted on • Edited on

ReactQuill with NextJS

Hey everyone, just wanted to share this with you. So I've been trying to find rich editors that were compatible with NextJS. Couldn't find any but I found a way to get ReactQuill working.

Import dynamic

import dynamic from 'next/dynamic'
Enter fullscreen mode Exit fullscreen mode

After that import ReactQuill using dynamic

const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
Enter fullscreen mode Exit fullscreen mode

Now you can easily use it!
Example:

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

function App() {
    const [value, setValue] = useState('')
    return(
       <ReactQuill value={value} onChange={setValue}/>
    )
}

export default App;
Enter fullscreen mode Exit fullscreen mode

I hope this helps 😊

Top comments (33)

Collapse
 
mamsheikh profile image
Munir Ali Mohammed

Arigato Guzaimas

Collapse
 
amreshsinha profile image
Amresh Prasad Sinha

Thanks a lot! You saved my time!

Collapse
 
cyishere profile image
CY

Thank you! This is very helpful.

Collapse
 
umschaudhary profile image
Umesh Chaudhary

How do you import ReactQuill modules (quill-image-resizing -module) on the dynamic imports ?

Collapse
 
foysaldev profile image
foysal

same pblm

Collapse
 
yvau profile image
Mobiot Yves André • Edited

i believe , you made a typpo in the beginning of your post

import dynamic from 'react/dynamic'
Enter fullscreen mode Exit fullscreen mode

(that library don't exist), instead of

import dynamic from 'next/dynamic';
Enter fullscreen mode Exit fullscreen mode

you corrected in your final code

Collapse
 
a7u profile image
A7U

Thank you for noticing. Fixed!

Collapse
 
yozawiratama profile image
Yoza Wiratama

can not use ref for image upload handler

Collapse
 
brodwen83 profile image
Wendell D. Enc • Edited

ref does not work with dynamic import. you can use this

dynamic import

const QuillNoSSRWrapper = dynamic(
  async () => {
    const { default: RQ } = await import('react-quill');
    // eslint-disable-next-line react/display-name
    return ({ forwardedRef, ...props }) => <RQ ref={forwardedRef} {...props} />;
  },
  { ssr: false }
);
Enter fullscreen mode Exit fullscreen mode

create ref
const quillRef = useRef(null)

jsx

<QuillNoSSRWrapper
            forwardedRef={quillRef}
            ...
          />
Enter fullscreen mode Exit fullscreen mode

then in image cllback handler you can now access the quill object and range
const quillObj = quillRef?.current?.getEditor();
const range = quillObj?.getSelection();

then append the image url after upload to the editor's current range

quillObj.editor.insertEmbed(range.index, 'image', data?.secure_url);

here the handler:

const quillImageCallback = async () => {
    const input = document.createElement('input');
    input.setAttribute('type', 'file');
    input.setAttribute('accept', 'image/*');
    input.click();

    input.onchange = async () => {
      const file = input.files ? input.files[0] : null;
      let data = null;
      const formData = new FormData();
      formData.append('upload_preset', 'fixer-upper-v1');

      const quillObj = quillRef?.current?.getEditor();
      const range = quillObj?.getSelection();

      if (file) {
        formData.append('file', file);
        formData.append('resource_type', 'raw');
        formData.append('folder', `- ${fileData?.name}`);

        const responseUpload = await fetch(
          `${process.env.NEXT_PUBLIC_IMAGE_UPLOAD}/upload`,
          { method: 'POST', body: formData }
        );

        data = await responseUpload.json();
        if (data.error) {
          console.error(data.error);
        }

        quillObj.editor.insertEmbed(range.index, 'image', data?.secure_url);
      }
    };
  };
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ivanfarkas profile image
Ivan Farkas • Edited

Getting an error for forwardedRef
Property 'forwardedRef' does not exist on type '{}'.ts(2339)

return ({ forwardedRef, ...props }) => <RQ ref={forwardedRef} {...props} />;
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
gaminirainmaker profile image
GaminiRainMaker

Same Issue I am facing @ivanfarkas .Did you got any solution to it.

Collapse
 
rommyarb profile image
L. Rommy Arbantas

Many thanks for this!! ❤️

Collapse
 
karthikeyand profile image
Karthikeyan Dharmalingam

Server Error
TypeError: Quill.register is not a function

This error happened while generating the page. Any console logs will be displayed in the terminal window.

dev-to-uploads.s3.amazonaws.com/up...

dev-to-uploads.s3.amazonaws.com/up...

Collapse
 
a7u profile image
A7U

Can you show me how you imported React-quill?

Collapse
 
uttam_vishvakarma_6eb6cc1 profile image
uttam vishvakarma

Did you solution to it?

Collapse
 
ababeelceo profile image
Abdulla

When i use on reactquill with nextjs each onchange the whole component re-render how can i resolve this

I have also tried the same the above mention but still the whole component re-render problem persist

Collapse
 
devankitkr profile image
Ankit kumar • Edited

toolbar is appearing twice. i don't know how to fix it.. i am using quill inside form element

Collapse
 
akyno profile image
Robersom Aquino

Add this CSS in your global style:

.quill > .ql-toolbar:first-child {
display: none !important;
}

Or in your next.config.js
reactStrictMode: false,

Collapse
 
marcelopatropi profile image
MarceloPatropi

SAme here. Didi you find a solution?

Collapse
 
mrcmesen profile image
Marco Mesen Campos • Edited

Simple solution, I guess not the best:

// component:
<Editor value={html} onChange={setHtml} id="html-editor" />

// css
  #html-editor > div:nth-child(1) {
    display: none !important;
  }
Enter fullscreen mode Exit fullscreen mode

I don't think it's the best either, but definitely better than the previous one

export function TextEditor(props: TextEditorProps) {
  const refElement = useRef<HTMLDivElement>(null);
  const [html, setHtml] = useState('The template will be here');

  useEffect(() => {
    if (refElement.current?.firstChild) {
      const children = Array.from(
        refElement.current.firstChild.childNodes
      ) as HTMLElement[];
      const duplicateTooltips = children.filter((el) => {
        return el.classList.contains('ql-toolbar');
      });
      if (duplicateTooltips.length > 1) {
        duplicateTooltips[0].remove();
      }
    }
  }, [refElement.current]);

  return (
    <div ref={refElement}>
      <Editor value={html} onChange={setHtml} />
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode
Collapse
 
reacher profile image
reachmeatanytime7

Thank you!! This helped me :)