Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server side rendering broken since Sep 20th commits #122

Closed
joewestcott opened this issue Sep 24, 2016 · 22 comments
Closed

Server side rendering broken since Sep 20th commits #122

joewestcott opened this issue Sep 24, 2016 · 22 comments

Comments

@joewestcott
Copy link

/node_modules/quill/dist/quill.js:2245
var elem = document.createElement('div');
ReferenceError: document is not defined
@alexkrolick
Copy link
Collaborator

You'll probably want to file this issue upstream with Quill. The calls to document are in the core editor module.

https://github.com/quilljs/quill/search?utf8=%E2%9C%93&q=document.createElement

@zenoamaro
Copy link
Owner

@alexkrolick is correct, Quill is tightly coupled to the browser at the moment.

What could be done is to render only a <textarea> on server side. This should be easily accomplished in user code, but I guess we could see if it's possible to add some support here.

@calvinte
Copy link

calvinte commented Feb 14, 2017

I was able to work around this limitation by instantiating react-quill only on the client. Note that import only works on the top level, so I use require.

I imagine the maintainers of this module should be able to prevent calls to the DOM when it's not available, without too much trouble. Big +1 for that change.

Here's my wrapper component to support isomorphic rendering:

import React, {Component} from 'react'

export default class FormHtmlEditor extends Component {
  constructor(props) {
    super(props)
    if (document) {
      this.quill = require('react-quill')
    }
  }

  render() {
    const Quill = this.quill
    if (Quill) {
      return (
        <Quill
          onChange={this.props.onChange}
          theme="bubble"
          value={this.props.value}
        />
      )
    } else {
      return null
    }
  }
}

@alexkrolick
Copy link
Collaborator

alexkrolick commented Feb 14, 2017

Yes, ES6 modules are static. You can still probably detect document in the render, though:

import React, {Component} from 'react'
import Quill from 'react-quill'

export default class FormHtmlEditor extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    if (document) {
      return (
        <Quill
          onChange={this.props.onChange}
          theme="bubble"
          value={this.props.value}
        />
      )
    } else {
      return <textarea value={this.props.value} />
    }
  }
}

Not sure if we want to add support to the module directly - seems a bit too automagicky to me at the moment.

@calvinte
Copy link

All of my components are bundled up with WebPack and sent from the server using renderToString. When quill is included in the server side bundle via import my server crashes with the folowing output:

/path/to/src/node_modules/quill/dist/quill.js:2377
  var elem = document.createElement('div');
             ^

ReferenceError: document is not defined
    at Object.<anonymous> (/path/to/src/node_modules/quill/dist/quill.js:2377:13)
    at __webpack_require__ (/path/to/src/node_modules/quill/dist/quill.js:36:30)
    at Object.<anonymous> (/path/to/src/node_modules/quill/dist/quill.js:1669:2)
    at __webpack_require__ (/path/to/src/node_modules/quill/dist/quill.js:36:30)
    at Object.<anonymous> (/path/to/src/node_modules/quill/dist/quill.js:76:15)
    at __webpack_require__ (/path/to/src/node_modules/quill/dist/quill.js:36:30)
    at Object.defineProperty.value (/path/to/src/node_modules/quill/dist/quill.js:7798:14)
    at __webpack_require__ (/path/to/src/node_modules/quill/dist/quill.js:36:30)
    at Object.<anonymous> (/path/to/src/node_modules/quill/dist/quill.js:63:19)
    at __webpack_require__ (/path/to/src/node_modules/quill/dist/quill.js:36:30)

My hack as above using require was to work around this crash.

@aldidana
Copy link

this is how to check if it run in a browser or not (i changed a code from @calvintennant ):

import React, { Component } from 'react'

export default class FormHtmlEditor extends Component {
  constructor(props) {
    super(props)
    if (typeof window !== 'undefined') {
      this.ReactQuill = require('react-quill')
    }
  }

  render() {
    const ReactQuill = this.ReactQuill
    if (typeof window !== 'undefined' && ReactQuill) {
      return (
        <ReactQuill
          onChange={this.props.onChange}
          theme="bubble"
          value={this.props.value}
        />
      )
    } else {
      return <textarea />;
    }
  }
}

@jdmswong
Copy link

I used if (!__SERVER__) instead, looks cleaner

@yalamber
Copy link

I wrote this with dynamic module

import { Spin } from 'antd';
import dynamic from 'next/dynamic';

const QuillNoSSRWrapper = dynamic(
  import('react-quill')
  {
    ssr: false,
    loading: () => <Spin />,
  }
);

export default QuillNoSSRWrapper;

@alexlafroscia
Copy link

I really liked the solution that @yalamber came up with, using next/dynamic, but I want to have the "placeholder" render the content that would eventually be in the editor, so that it shows the content while the page is hydrating. Unfortunately you don't get access to the props that you pass to QuillNoSSRWrapper within the loading component 😦

@samlogan
Copy link

samlogan commented Dec 2, 2019

This is the cleanest method I have found to import modules that use window or document:

const ReactQuill = typeof window === 'object' ? require('react-quill') : () => false;

// Use ReactQuill as desired

<ReactQuill
  value={value}
  onChange={handleChange}
/>

@bum-hyun
Copy link

bum-hyun commented Mar 8, 2020

I wrote this with dynamic module

import { Spin } from 'antd';
import dynamic from 'next/dynamic';

const QuillNoSSRWrapper = dynamic(
  import('react-quill')
  {
    ssr: false,
    loading: () => <Spin />,
  }
);

export default QuillNoSSRWrapper;

this solution makes the situation what is re-rendering when onChange event performed

@frietkip
Copy link

thanks @bum-hyun.

Do you have an example code on how to implement this module? Where do I import QuillNoSSRWrapper?

@cemre-2187
Copy link

This is the cleanest method I have found to import modules that use window or document:

const ReactQuill = typeof window === 'object' ? require('react-quill') : () => false;

// Use ReactQuill as desired

<ReactQuill
  value={value}
  onChange={handleChange}
/>

You are right. But It is not working like textarea. How can ı pass form data via this editor.

@Kamez
Copy link

Kamez commented Jul 6, 2020

How about resize image ?

I'm using quill-image-resize-module, but it's not working with NextJS.

@frietkip
Copy link

frietkip commented Jul 7, 2020

@Kamez you can try this solution. This worked for my application.

In my eventform I had a field like this:

<Field
            name='body'
            component={renderQuill}
            placeholder='Tell your story'
          />

The component renderQuill has the following setup:

renderQuill.js

import React from 'react'
import dynamic from 'next/dynamic'


const QuillNoSSRWrapper = dynamic(
  () => import('./renderQuillReact'),
  { ssr: false, loading: () => <p>Loading ...</p> },
)

class renderQuill extends React.Component {
  render () {
    const { input, label, type } = this.props
    return (
        <QuillNoSSRWrapper input={input} label={label} type={type}/>
    )
  }
}


export default renderQuill

As you notice I import a second component './renderQuillReact' that contains the resize module.

renderQuillReact.js component

import React from "react";
import ReactQuill, { Quill } from "react-quill";
import ImageResize from "quill-image-resize-module-react"; // import as default
import { ImageDrop } from "quill-image-drop-module";
Quill.register("modules/imageResize", ImageResize);
Quill.register("modules/imageDrop", ImageDrop);

class renderQuillReact extends React.Component {
    render () {
  
      const { input, label, type } = this.props
      return (
        <ReactQuill
        modules={Event.modules}
        label={label}
        formats={Event.formats}
        {...input}
        type={type}
        onChange={(newValue, delta, source) => {
          if (source === "user") {
            input.onChange(newValue);
          }
        }}
        onBlur={(range, source, quill) => {
          input.onBlur(quill.getHTML());
        }}
      />
      )
    }
  }


Event.modules = {
  toolbar: [
    [{ header: "1" }, { header: "2" }, { font: [] }],
    ["bold", "italic", "underline", "blockquote"],
    [{ list: "ordered" }, { list: "bullet" }],
    ["link", "image", "video"],
    ["clean"],
    ["code-block"]
  ],
  imageDrop: true,
  imageResize: {
    handleStyles: {
      backgroundColor: "black",
      border: "none",
      color: "white"
    },
    modules: ["Resize", "DisplaySize", "Toolbar"]
  }
};

Event.formats = [
  "header",
  "font",
  "size",
  "bold",
  "italic",
  "underline",
  "blockquote",
  "list",
  "bullet",
  "link",
  "image",
  "video",
  "code-block"
];

export default renderQuillReact;

Hope this solution works for you!

@Kamez
Copy link

Kamez commented Jul 8, 2020

@frietkip Thank you very much.

@imsks
Copy link

imsks commented Jul 8, 2020

This is the cleanest method I have found to import modules that use window or document:

const ReactQuill = typeof window === 'object' ? require('react-quill') : () => false;

// Use ReactQuill as desired

<ReactQuill
  value={value}
  onChange={handleChange}
/>

I am facing an error saying Can't read the property 'import' of undefined

@jonsoku-dev jonsoku-dev mentioned this issue Aug 17, 2020
6 tasks
@noobnoobdc137
Copy link

This is what i did works fine with SSR on next js

import { SPLayout } from '@layout';
import dynamic from 'next/dynamic';
import { FC, ReactElement } from 'react';

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

const Create: FC = (): ReactElement => {
	return (
		<SPLayout>
			<ReactQuill
				value={'test'}
				onChange={e => {
					console.log(e);
				}}
			/>
		</SPLayout>
	);
};

export default Create;

@seemX17
Copy link

seemX17 commented Jul 14, 2021

This is the cleanest method I have found to import modules that use window or document:

const ReactQuill = typeof window === 'object' ? require('react-quill') : () => false;

// Use ReactQuill as desired

<ReactQuill
  value={value}
  onChange={handleChange}
/>

This is the best solution! @samlogan Thanks!!

@ismaelheinen
Copy link

Not using next on my application, and this solution not worked for me.

@peteromano
Copy link

Same (nuxt3, SSR).. using ES modules, cant use require, and dynamic import() seems to be behaving strangely with a condition..

Just curious, why cant quill just do nothing if its not running in the client?

@OrkhanGG
Copy link

Following solution works like a charm:

    const QuillNoSSRWrapper = dynamic(import('react-quill'), {
        ssr: false,
        loading: () => <p>Loading ...</p>,
    })
    return(
QuillNoSSRWrapper theme="snow"/>)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests