NextJS + Simple MDE + React Forms Hook Server Error

My form works, and stores to the database, but I get this server error when trying to use both the Smple MDE with the React Forms Hook Controller, but not when I use either one alone.

The error:

 node_modules/codemirror/lib/codemirror.js (88:2) @ eval
 ⨯ ReferenceError: document is not defined
    at __webpack_require__ (/Users/suzanne2022/Developer/React/tricoach24/.next/server/webpack-runtime.js:33:43)
    at __webpack_require__ (/Users/suzanne2022/Developer/React/tricoach24/.next/server/webpack-runtime.js:33:43)
    at __webpack_require__ (/Users/suzanne2022/Developer/React/tricoach24/.next/server/webpack-runtime.js:33:43)
    at eval (./app/tests/new/page.tsx:11:80)
    at (ssr)/./app/tests/new/page.tsx (/Users/suzanne2022/Developer/React/tricoach24/.next/server/app/tests/new/page.js:294:1)
    at __webpack_require__ (/Users/suzanne2022/Developer/React/tricoach24/.next/server/webpack-runtime.js:33:43)
    at JSON.parse (<anonymous>)
null

My Package.JSON file (I like to install ‘latest’ for everything and try to troubleshoot, as once i’m done with a tutorial I’d have to figure out how to update anyway)

{
  "name": "tricoach24",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@next-auth/prisma-adapter": "^1.0.7",
    "@prisma/client": "^5.7.1",
    "@radix-ui/themes": "^2.0.3",
    "@vercel/postgres": "^0.5.1",
    "axios": "^1.6.5",
    "classnames": "^2.3.2",
    "daisyui": "^4.5.0",
    "easymde": "^2.18.0",
    "next": "14.0.4",
    "next-auth": "^4.24.5",
    "nodemailer": "^6.9.8",
    "react": "^18",
    "react-dom": "^18",
    "react-hook-form": "^7.49.2",
    "react-simplemde-editor": "^5.2.0",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "autoprefixer": "^10.0.1",
    "eslint": "^8",
    "eslint-config-next": "14.0.4",
    "postcss": "^8",
    "prisma": "^5.7.1",
    "tailwindcss": "^3.3.0",
    "typescript": "^5"
  }
}

My /new page: (I’m doing tests, but it’s the issues page code)

'use client'

import {useCallback, useState} from 'react'
import {TextFieldRoot, TextFieldInput, Button} from "@radix-ui/themes";
// import  {SimpleMdeReact} from "react-simplemde-editor";

import SimpleMDE  from "react-simplemde-editor"
import "easymde/dist/easymde.min.css";

import {Controller, useForm} from "react-hook-form";
import axios from "axios";
import {useRouter} from "next/navigation";

interface TestForm {
    activity: string,
    comment: string
}

const NewTestPage = () => {
    const router = useRouter()
    const {register, control, handleSubmit} = useForm<TestForm>()
    const [value, setValue] = useState("");

    const onChange = useCallback((value: string) => {
        setValue(value);
    }, []);



    return (
        <form className="max-w-xl space-y-3"
              onSubmit={handleSubmit(async (data) => {
                      try {
                          await axios.post('/api/tests', data)
                          router.push('/tests')
                      } catch (error) {
                          console.log(error)
                      }

                  }
              )}
        >
            <h2>New Fitness Test</h2>
            <TextFieldRoot>
                <TextFieldInput placeholder='Activity' {...register('activity')}/>
            </TextFieldRoot>

            {document &&
                <Controller
                    name="comment"
                    control={control}
                    render={({field}) =>
                     (
                         <SimpleMDE placeholder='Comments' {...field}/>
                     )
                }
                />
            }

            <Button>Submit New Test</Button>
        </form>
    )
}
export default NewTestPage

I havn’t been able to track down if there is an in compatibility between react hook forms and react simple MDE…in fact very few results when I search for that specifically. Appreciate any suggestions.

I’ve tried doing a check for document before displaying the Controller compopnent but that doesn’t seem to help.

You can view the lesson 9 of “Viewing Issue”, titled “Disabling SSR”
Explanation is on the video.

Basically, to solve this, you would need to use dynamic loading (lazy loading) to disable serve-side rendering for the simpleMDE component

const SimpleMDE = dynamic(() => import(“react-simplemde-editor”), {
ssr: false,
});

2 Likes

Server side code has access to different capabilities compared to client side code.

The client side code can access the document model inside the browser.

Anything you create that needs to access the browser’s capabilities, like MDE, must be rendered in the client side.

Anything you render on the server will likely perform faster and can be better optimized by node.js. But server side renders cannot talk to the document object or the browser.

Jerry

1 Like

Thank you, i had not gotten to that lesson yet. But now that I have the server error has dissapeard, and I have a some new errors that have popped up:

app-index.js:34 Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

I’m also getting a codemirror error on the browser:
codemirror.js:550 [Violation] Added non-passive event listener to a scroll-blocking 'touchstart' event. Consider marking event handler as 'passive' to make the page more responsive. See https://www.chromestatus.com/feature/5745543795965952

Is this a version issue? I really don’t know enough about codemirror to try and track something down with the SimpleMDE. I am not seeing these errors mentioned here. If anyone is game for it, here is my github repo…the component is tests and not issues as I’m doing parallel coding with some different ideas.

https://github.com/AdventureBear/tricoach24

@AdventureBear were you able to figure it out?

I get a similar error on the server when I refresh the /issues/new page.

 ⨯ node_modules/codemirror/lib/codemirror.js (18:0) @ eval
 ⨯ ReferenceError: navigator is not defined
    at __webpack_require__ (/Users/georgevuong/Documents/sides/nextjs/issue-tracker/.next/server/webpack-runtime.js:33:43)
    at __webpack_require__ (/Users/georgevuong/Documents/sides/nextjs/issue-tracker/.next/server/webpack-runtime.js:33:43)
    at __webpack_require__ (/Users/georgevuong/Documents/sides/nextjs/issue-tracker/.next/server/webpack-runtime.js:33:43)
    at eval (./src/app/issues/new/page.tsx:8:80)
    at (ssr)/./src/app/issues/new/page.tsx (/Users/georgevuong/Documents/sides/nextjs/issue-tracker/.next/server/app/issues/new/page.js:283:1)
    at __webpack_require__ (/Users/georgevuong/Documents/sides/nextjs/issue-tracker/.next/server/webpack-runtime.js:33:43)
    at JSON.parse (<anonymous>)
null

I commented out various components and refreshed the page to see what component could be causing it, and it seems like it’s coming from <SimpleMDE />.
I tried @Andy214’s suggestion and althought the navigation is not defined is no longer appearing, I am now getting the same error as you on the console.

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Check the render method of `Controller`.

Hey @George1 ,

I faced the exact same issue as you and this is how I fixed it.

'use client';
import { AiFillBug } from 'react-icons/ai';
import { Button, TextField, Heading, Callout } from '@radix-ui/themes';
import { useForm, Controller } from 'react-hook-form';
import React, { useState } from 'react';
import dynamic from 'next/dynamic';
import axios from 'axios';

const SimpleMDE = dynamic(() => import('react-simplemde-editor'), {
  ssr: false,
  loading: () => <p>Loading...</p>,
});
import 'easymde/dist/easymde.min.css';
import { useRouter } from 'next/navigation';

interface IssueForm {
  title: string;
  description: string;
}

const NewIssuePage = () => {
  const { register, control, handleSubmit } = useForm<IssueForm>();
  const [codeError, setCodeError] = useState('');

  const handleSubmission = async (data: IssueForm) => {
    try {
      await axios.post('/api/issues', data);
      router.push('/issues');
    } catch (error) {
      console.log(error);
      setCodeError('An error has occured');
    }
  };

  const router = useRouter();
  return (
    <div className="space-y-3 max-w-xl">
      {codeError && (
        <Callout.Root color="red" size="1">
          <Callout.Icon>
            <AiFillBug />
          </Callout.Icon>
          <Callout.Text>{codeError}</Callout.Text>
        </Callout.Root>
      )}
      <form
        className="space-y-3 items-center"
        onSubmit={handleSubmit(handleSubmission)}
      >
        <Heading>Add a new issue</Heading>
        <TextField.Root
          variant="classic"
          placeholder="Enter the title"
          max={20}
          {...register('title')}
        />
        <Controller
          name="description"
          control={control}
          render={({ field }) => {
            const { ref, ...rest } = field; // removes ref
            return <SimpleMDE placeholder="Enter the description" {...rest} />;
          }}
        />
        <Button>Submit new issue</Button>
      </form>
    </div>
  );
};

export default NewIssuePage;

So the things to look out for is that I’ve implemented lazy loading import for SimpleMDE component and have set ssr to false. Then finally, I have implemented a small function inside the <Controller> component’s render property to remove ref.

Hope this helps :slight_smile: .