Encounter the error "Document is not defined" while attempting to import the 'ui/icons' from the React Quill library within a NextJS environment

After making some adjustments by importing Quill.import('ui/icons') to override the default icons in the library, I encountered an error stating "document is not defined". Is there a workaround for this issue?

import { Stack } from '@mui/material'
import * as React from 'react'
import { useMemo, useState } from 'react'
import 'react-quill/dist/quill.snow.css'
import dynamic from 'next/dynamic'
import { RedoIcon } from 'libs/icons'
import { Quill } from 'react-quill'

interface Props {}

const modules = {
  toolbar: [
    ['undo', 'redo'],
    ['bold', 'italic', 'underline', 'strike'], // toggled buttons
    ['blockquote', 'code-block'],
}

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

  const icons = Quill.import('ui/icons')
  icons['undo'] = <RedoIcon />

  return (
    <Stack>
      {typeof window !== 'undefined' && (
        <ReactQuill value={value} onChange={setValue} modules={modules} />
      )}
    </Stack>
  )
}

export default CustomQuill

Answer №1

Acknowledgement & Credit -

QuillEditor.constants.ts

export const TOOLBAR_ICONS = {
  BOLD_ICON: `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="currentColor"><path d="M20.5 13.5C20.5 13.0404 20.4095 12.5852 20.2336... (truncated for brevity)
};

export const FORMATS = ['bold', 'italic', 'underline', 'link', 'list'];

export const TOOLBAR_ID = 'quill_toolbar_id';

export const MODULES = {
  toolbar: `#${TOOLBAR_ID}`,
};

export const TEXT_AREA_HEIGHT = 200;

QuillEditorComponent.tsx

import dynamic from 'next/dynamic';
import { LegacyRef, JSX } from 'react';
import type ReactQuill from 'react-quill';

import { TOOLBAR_ICONS } from './QuillEditor.constants.ts';

interface IWrappedComponent extends React.ComponentProps<typeof ReactQuill> {
  forwardedRef: LegacyRef<ReactQuill>;
}

const ReactQuillBase = dynamic(
  async () => {
    const { default: RQ } = await import('react-quill');

    function QuillJS({ ...props }: React.ComponentProps<typeof ReactQuill>) {
      const icons = RQ.Quill.import('ui/icons');
      icons.bold = TOOLBAR_ICONS.BOLD_ICON;
      icons.italic = TOOLBAR_ICONS.ITALIC_ICON;
      icons.underline = TOOLBAR_ICONS.UNDERLINE_ICON;
      icons.link = TOOLBAR_ICONS.LINK_ICON;
      icons.list = {
        bullet: TOOLBAR_ICONS.LIST_BULLETS_ICON,
        ordered: TOOLBAR_ICONS.LIST_ORDERED_ICON,
      };

      return <RQ {...props} />;
    }

    return QuillJS;
  },
  {
    ssr: false,
  },
);

export function QuillEditorComponent(
  params: JSX.IntrinsicAttributes &
    IWrappedComponent & {
      minHeight: number;
      maxHeight: number;
    },
) {
  return (
    <>
      <ReactQuillBase {...params} />
    </>
  );
}

QuillToolbarComponent.tsx

import { TOOLBAR_ID } from './QuillEditor.constants';

// https://quilljs.com/docs/modules/toolbar/
export const QuillToolbarComponent = () => {
  return (
    <div id={TOOLBAR_ID}>
      <div className="ql-formats">
          <button className="ql-bold" />
          <button className="ql-italic" />
          <button className="ql-underline" />
          <button className="ql-link" />
          <button className="ql-list" value="bullet" />
          <button className="ql-list" value="ordered" />
      </div>
    </div>
  );
};

UsageExample.tsx

<>
  <QuillToolbarComponent />
  <QuillEditorComponent
        forwardedRef={quillTextAreaRef}
        defaultValue={quillValue as DeltaStatic}
        onChange={handleChange}
        modules={MODULES}
        minHeight={TEXT_AREA_HEIGHT}
        maxHeight={TEXT_AREA_HEIGHT}
        onKeyDown={handleKeyDown}
        onBlur={handleBlur}
        placeholder={placeholder}
        onFocus={handleFocus}
        formats={FORMATS}
      />
</>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for effectively handling global states using React Query

I have a project that utilizes Nextjs and Supabase. Previously, I used context API but now I am attempting to switch to React Query. However, this transition is proving to be quite challenging for me. Can I completely replace context with React Query? One ...

Guide to Triggering a Page View Event in Google Tag Manager with Angular

Previously, I manually fired the Page View Event using JavaScript from app.component.ts while directly accessing Google Analytics: declare var gtag: Function; ... constructor(private router: Router) { const navEndEvents = this.router.events.pipe( fil ...

Display issues with deeply nested components

I'm facing an issue with displaying the third nested component. Expected: Hello App Component Hello Nest-A Component Hello Nest-1 Component Hello Test-Z Component Actual: Hello App Component Hello Nest-A Component Hello Nest-1 Component Why ...

Converting a string to Time format using JavaScript

I am working with a time format of 2h 34m 22s which I need to parse as 02:34:22. Currently, I am achieving this using the following code: const splitterArray = '2h 34m 22s'.split(' '); let h = '00', m = '00', s = &a ...

Customizing colors does not show the default color options

A new custom color named twitterBlue has been incorporated into the tailwind.config.ts file and is displaying correctly. However, the default colors are no longer rendering as expected (e.g., the background should be black and the hover state should turn ...

Exporting third-party types in an npm package

I recently developed an npm package that relies on types from the DefinitelyTyped repository. I added these types as devDependencies in the npm package and was able to use them without any issues, like so: export class Example { constructor (options: Ex ...

Verify in typescript if type A is equal to either type B or type C

Within one specific file, there is a structured code block like the following: export const _total = { a: '', b: '', c: '', d: '', e: '', f: '', } type TotalKeysType = typeof _all; ex ...

Encountering a snag while attempting to launch NextJS application on Vercel platform

I've been facing an issue while attempting to deploy my NextJS application on Vercel. The error message I keep encountering is as follows: 19:07:34.256 > Build error occurred 19:07:34.257 Error: Failed to load config "next/babel" to exten ...

typescriptIn React Router v5 with TypeScript, an optional URL parameter is implemented that can have an undefined

I'm currently working with react-router v5.1 and TypeScript, and I have set up the following route configurations: <Router basename="/" hashType="slash"> <Switch> <Route path="/token/:tokenName"> <TokenPag ...

What could be causing the "Module not found" error in relation to the favicon.ico file when launching a fresh Next.js 14 project with create-next-app?

After running the command below to start a new Next.js app: npx create-next-app@latest I encountered an error when trying to run npm run dev: Module not found: Can't resolve 'C:\xxxxx\xxxxx\xxxxx\my-app\src\app&bsol ...

The file isn't located in 'rootDir', even though all the details seem to be accurate

I'm currently troubleshooting an issue with one package in my nx monorepo that is causing the error code "TS6059". Interestingly, all other packages are functioning correctly in previous builds. After reviewing my index.ts files, it appears that all ...

"Unexpected Type Inference Issue: A variable initially defined as a string inexplicably transforms into 'undefined'

Currently, I am incorporating the await-to-js library for handling errors (specifically utilizing the to method from the library). In an intriguing scenario, the variable type shifts to string | undefined within a for..of loop, whereas outside of the loop ...

I am eager to perform DOM manipulation similar to jQuery but within the context of Angular 6

Is there a way to modify the background color of the main div when a button is clicked? <div> <p>I'd like to be able to change the background color of the parent div by clicking a certain button. </p> <button (click)=" ...

Can the type definition be shared between React and AWS CDK when using Node.js?

I am currently building an application with React frontend and AWS CDK backend, using TypeScript. Due to the usage of GraphQL in certain parts of the application, I am finding myself duplicating type definitions extensively. For instance, when creating a ...

What is the most efficient way to set default props for a component?

According to the official documentation, the best practice for setting default props for a component is as follows: interface Props { /** * default value set to 100 */ size?: number } function Avatar({ size = 100 }: Props) { // ... } An ...

I am encountering unexpected behavior with NextJS's getInitialProps function, as it is giving me a compiler error stating "varName not found on type {}"

I seem to be stuck on a simple syntax issue while working with NextJs. I am attempting to perform dynamic server-side fetches using the getInitialProps pattern. However, the compiler is unable to recognize the return of getInitialProps in the regular func ...

Styling with Styled Components in Next.js 9+ to Prevent FOUC (Flash of Unstyled Content)

After researching solutions and ideas from various sources, such as SA and Reddit, I have been unable to resolve an issue that causes the HTML to load without any styling before being injected into the DOM on both production and local environments. Below ...

Leverage local JSON file data in HTML using TypeScript and Angular 7 for enhanced functionality

I am looking to incorporate a basic local JSON file into my Angular 7 project and utilize the data within my HTML file. Just a straightforward example. The JSON file is named data.json. I aim to retrieve the information from this JSON file in app.component ...

Creating a variable as a list of string arrays in TypeScript

Currently working with Angular 2.0, I am trying to declare a variable in a Typescript file that is a list of string arrays. I attempted using yAxis_val: list, but it is not functioning correctly. If anyone knows the proper way to achieve this, please prov ...

Tips for maintaining i18n locale slugs and ensuring i18n consistency when reloading in Next.js

I'm currently utilizing next-translate. The default recognition of my routes is as follows: /about <--- /de/about /es/about However, I would like to set a specific locale for all paths: /en/about <--- /de/about /es/about Below is ...