react-md-editor: Does not work with NextJS

NextJS does not allow global CSS to be imported at the component level, only in _app.js.

error - ./node_modules/@uiw/react-md-editor/lib/esm/index.css
Global CSS cannot be imported from within node_modules.
Read more: https://err.sh/next.js/css-npm

See https://codesandbox.io/s/compassionate-cookies-msmsd?file=/pages/index.js

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 20
  • Comments: 59 (15 by maintainers)

Commits related to this issue

Most upvoted comments

I created a nextjs package next-remove-imports to solve the problem.

Example: https://codesandbox.io/s/nextjs-example-react-md-editor-qjhn7?file=/pages/index.js
Example: @uiwjs/next-remove-imports/example
Example: https://next-remove-imports-example.vercel.app

⚠️ https://www.npmjs.com/package/next-transpile-modules

@rafaelguedes @serendipity1004 @karlhorky @namnd @A7U @stLoiz @veksen @earthnoob @jdudmesh @diego9497

Open in CodeSandbox Open in StackBlitz

npm i @uiw/react-md-editor@v3.6.0
npm i next-remove-imports
// next.config.js
const removeImports = require("next-remove-imports")();

module.exports = removeImports({
  // ✅  options...
});
import "@uiw/react-md-editor/markdown-editor.css";
import "@uiw/react-markdown-preview/markdown.css";
import dynamic from "next/dynamic";
import { useState } from "react";

const MDEditor = dynamic(
  () => import("@uiw/react-md-editor").then((mod) => mod.default),
  { ssr: false }
);
const EditerMarkdown = dynamic(
  () =>
    import("@uiw/react-md-editor").then((mod) => {
      return mod.default.Markdown;
    }),
  { ssr: false }
);

function HomePage() {
  const [value, setValue] = useState("**Hello world!!!**");
  return (
    <div data-color-mode="dark">
      <MDEditor value={value} onChange={setValue} />
      <div style={{ paddingTop: 50 }}>
          <EditerMarkdown source={value} />
      </div>
    </div>
  );
}

export default HomePage;

+ import * as commands from "@uiw/react-md-editor/lib/commands";
- import { commands } from "@uiw/react-md-editor"
- // or
- import * as commands from "@uiw/react-md-editor/esm/commands";

example: https://codesandbox.io/s/nextjs-example-react-md-editor-forked-89jwvb?file=/pages/index.js

import * as commands from "@uiw/react-md-editor/lib/commands";

import { MDEditorProps } from '@uiw/react-md-editor';
import * as commands from '@uiw/react-md-editor/esm/commands';

+ const MDEditor = dynamic<MDEditorProps>(() => import('@uiw/react-md-editor'), {
+   ssr: false,
+ });

function HomePage() {
-  const MDEditor = dynamic<MDEditorProps>(() => import('@uiw/react-md-editor'), {
-    ssr: false,
-  });
  return (
    <div>
      <MDEditor
         value="**Hello world!!!**"
         commands={[
           commands.bold,
           commands.italic,
         ]}
      />
    </div>
  );
}

Solution to > error - ./node_modules/@uiw/react-md-editor/lib/esm/index.css Global CSS cannot be imported from within node_modules. Read more: https://err.sh/next.js/css-npm

In your next.js component or page, add this>

import '@uiw/react-md-editor/dist/markdown-editor.css'

import '@uiw/react-md-editor/lib/esm/components/DragBar/index.css'

import '@uiw/react-md-editor/lib/esm/components/TextArea/index.css'

import '@uiw/react-md-editor/lib/esm/components/Toolbar/index.css'

import '@uiw/react-md-editor/lib/esm/components/Toolbar/Child.css'

Then, go to node_modules/@uiw/ and comment all imports of .css files. It will start working. It worked for me on next 10.0.7 😃

@jaywcjlove Can you just remove the imports from your package, and ask us to import it ourselves? Your solution with next-remove-imports is not working for me.

Yes. It’s almost impossible to suit all the needs of so many frameworks these days and I thank you so much for taking time and effort to build a very beautiful open source. However, if you want your package to grow (I don’t know if this is your intention) it would be a good idea to encapsulate NextJS users because it is one of the biggest SSR framework. I don’t think this is something you should be unhappy about. You should be happy because it just means that your package is gaining a lot of popularity. That’s why there are demands here and there.

For those struggling, I was able to fix this issue without installing next-remove-imports by adding the line transpilePackages: ['react-md-editor'] to my next.config.js file.

I was having the exact problem and managed to fix by installing @zeit/next-css and updated next.config.js:

const withCSS = require('@zeit/next-css');

module.exports = withCSS({
  // Your configurations here
});

Hope that helps you, let me know if it works.

solutions are 👍

install :- npm install next-remove-imports npm install @uiw/react-md-editor@v3.6.0

// next.config.js file write :- const removeImports = require(‘next-remove-imports’)(); module.exports = removeImports({});

//File :- import “@uiw/react-md-editor/markdown-editor.css”; import “@uiw/react-markdown-preview/markdown.css”; import dynamic from “next/dynamic”; import { useState } from “react”;

const MDEditor = dynamic( () => import(“@uiw/react-md-editor”), { ssr: false } );

function Pages() { const [value, setValue] = useState(“Hello world!!!”); return ( <div> <MDEditor value={value} onChange={setValue} /> </div> ); } export default Pages;

I created a nextjs package next-remove-imports to solve the problem.

Example: https://codesandbox.io/s/nextjs-example-react-md-editor-qjhn7?file=/pages/index.js

@rafaelguedes @serendipity1004 @karlhorky @namnd @A7U @stLoiz @veksen @earthnoob @jdudmesh @diego9497

Open in CodeSandbox Open in StackBlitz

npm i @uiw/react-md-editor@v3.6.0
npm i next-remove-imports
// next.config.js
const removeImports = require("next-remove-imports")();

module.exports = removeImports({
  // ✅  options...
});
import "@uiw/react-md-editor/markdown-editor.css";
import "@uiw/react-markdown-preview/markdown.css";
import dynamic from "next/dynamic";
import { useState } from "react";

const MDEditor = dynamic(
  () => import("@uiw/react-md-editor").then((mod) => mod.default),
  { ssr: false }
);
const EditerMarkdown = dynamic(
  () =>
    import("@uiw/react-md-editor").then((mod) => {
      return mod.default.Markdown;
    }),
  { ssr: false }
);

function HomePage() {
  const [value, setValue] = useState("**Hello world!!!**");
  return (
    <div data-color-mode="dark">
      <MDEditor value={value} onChange={setValue} />
      <div style={{ paddingTop: 50 }}>
          <EditerMarkdown source={value} />
      </div>
    </div>
  );
}

export default HomePage;

@pascuflow example: https://codesandbox.io/s/nextjs-example-react-md-editor-forked-89jwvb?file=/pages/index.js

- import { commands } from "@uiw/react-md-editor";
+ import * as commands from "@uiw/react-md-editor/esm/commands";

I used the plugin but I’m still encountering an error. The editor/preview seems to work fine when I navigate to the page with it via <Link />, however if I refresh that page I get the following error:

Server Error
require() of ES Module /home/axel/dev/lit-app/node_modules/react-markdown/index.js from /home/axel/dev/lit-app/node_modules/@uiw/react-markdown-preview/lib/index.js not supported.
Instead change the require of /home/axel/dev/lit-app/node_modules/react-markdown/index.js in /home/axel/dev/lit-app/node_modules/@uiw/react-markdown-preview/lib/index.js to a dynamic import() which is available in all CommonJS modules.

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

Given the snippet, how would one go about import commands and markdownUtils (such as selectWord)? The default exported component works but I can’t seem to figure out how to import anything else. Attempting to import anything else gives this as an error.

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module

@matteoturini The example still works: https://codesandbox.io/s/nextjs-example-react-md-editor-qjhn7?file=/pages/index.js

"dependencies": {
  "@uiw/react-md-editor": "3.6.5",
  "next": "11.1.2",
  "next-remove-imports": "1.0.6",
  "react": "17.0.2",
  "react-dom": "17.0.2"
},

But it’s not a good, long-term, robust solution.

@jaywcjlove I would again recommend switching to get users to import the styles themselves: https://github.com/uiwjs/react-md-editor/issues/52#issuecomment-805623260

1.Create a /static folder at the same level the /pages folder. 2. In that folder put your .css files 3. In your page components import Head and add a <link /> to your CSS.

import React from 'react';
import Head from 'next/head';

export default () => (
  <div>
    <Head>
      <title>My styled page</title>
      <link href="/static/styles.css" rel="stylesheet" />
    </Head>
    <p className="some-class-name">
      Hello world!
    </p>
  </div>
)

Reference: https://github.com/vercel/next.js/issues/299#issuecomment-263146962

As @preetpatel mentioned the only thing you have to do is use:

experimental: {
    esmExternals: 'loose',
  },

Here I leave the code that I used in a NextJS 13.0.7 App:

next.config.js

// @ts-check
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',   /* This is not part of the react-md-editor configuration */
  reactStrictMode: true,   /* This is not part of the react-md-editor configuration */
  swcMinify: true,   /* This is not part of the react-md-editor configuration */
  experimental: {
    appDir: true,   /* This is not part of the react-md-editor configuration */
    esmExternals: 'loose',   /* For react-md-editor */
  },
  },
};

module.exports = nextConfig;

page.tsx (Component)

'use client';
import MDEditor, { commands } from '@uiw/react-md-editor';
import { ChangeEvent, useState } from 'react';

/* Custom button in toolbar */
const help = {
  name: 'help',
  keyCommand: 'help',
  buttonProps: { 'aria-label': 'Insert help' },
  icon: (
    <svg viewBox='0 0 16 16' width='12px' height='12px'>
      <path
        d='M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8Zm.9 13H7v-1.8h1.9V13Zm-.1-3.6v.5H7.1v-.6c.2-2.1 2-1.9 1.9-3.2.1-.7-.3-1.1-1-1.1-.8 0-1.2.7-1.2 1.6H5c0-1.7 1.2-3 2.9-3 2.3 0 3 1.4 3 2.3.1 2.3-1.9 2-2.1 3.5Z'
        fill='currentColor'
      />
    </svg>
  ),
  // eslint-disable-next-line no-unused-vars
  execute: (state: any, api: any) => {
    window.open('https://www.markdownguide.org/basic-syntax/', '_blank');
  },
};

export default function Page() {
  const [value, setValue] = useState<string | undefined>('**Hello world!!!**');

  const handleChange = (
    value?: string | undefined,
    // eslint-disable-next-line no-unused-vars
    event?: ChangeEvent<HTMLTextAreaElement> | undefined,
  ) => {
    setValue(value);
    console.log('markdown: ', value);
  };

  return (
    <div>
      <MDEditor
        value={value}
        onChange={handleChange}
        commands={[...commands.getCommands(), help]}
      />
      {/* (val) => setValue(val) */}
    </div>
  );
}

As you can see there is no need to install additional packages like next-remove-imports or use specific NextJS imports for the component.

What worked for me (Next.js version 13.4.3) is inside your next.config.mjs add the following to your config:

...
experimental: {
    esmExternals: 'loose',
  },
....

For anyone is still looking for a temporary solution, I’ve created a forked package out of this one https://www.npmjs.com/package/@namskiiiii/react-md-editor-naked just to remove the styling

The suggested solution is now deprecated by Zeit/Vercel. Consider a proper fix and not import the CSS in the node_modules 😃

i got types error with typescript . ice_screenshot_20230727-112721

I just wanted to drop a quick heads up on style imports! it looks like they are not bundled with this name any more:

import "@uiw/react-md-editor/dist/markdown-editor.css";
import "@uiw/react-markdown-preview/dist/markdown.css";

instead, they are:

import "@uiw/react-md-editor/dist/mdeditor.min.css";
import "@uiw/react-markdown-preview/dist/markdown.min.css";

here is a screenshot of dist folder inside of node-modules that got me for 2 mins to figure it out:

Screenshot 2022-07-09 at 11 00 25

Just to consolidate the solution: Yes, the current solution works! Even so I kept getting errors, but the reason was simply the wrong use of the “next-remove-imports” plugin, when I needed to export more than one module in next.config.js . I was forgetting to put “options” as argument to “next-remove-imports”. I will put a clean and simple code for understanding.

Wrong mode:

const removeImports = require('next-remove-imports')({});
                  
module.exports = removeImports({})

module.exports = {
     images: {
         domains: [
             'localhost',
             '10.0.0.164',
             'example.com'
         ]
     }
}

Correct mode:

const removeImports = require('next-remove-imports')({})

module.exports = removeImports({
     images: {
         domains: [
             'localhost',
             '10.0.0.164',
             'example.com'
         ]
     }
})

I agree with @serendipity1004. NextJS is one of the biggest SSR frameworks and I believe that not encapsulating its users will prevent this amazing package to grow. I’m also trying to make this work with NextJS and I’m struggling. Can you please merge the PR #131 assuming that it will fix this issue? @jaywcjlove

The problem is that MDEditor.tsx has an import for index.less. The component will only work with NextJS if this import is removed. This requires changing react-md-editor. Consider moving the CSS for the component into a separate file which can be imported independently i.e. in _app.js.