react-editor-js
react-editor-js copied to clipboard
Support for SSR
Hello.
This module doesn't support server side rendering. When building on Netlify or on local production, I get an error that says "'window' is not available during server side rendering", and a bunch of code that is returned from the EditorJS plugin(s).
This is how I currently fixed this:
const MyComponent = () => {
if (typeof window !== 'undefined') {
const Editor = require('./editor').default;
return <Editor />;
}
return null;
};
Even though this works, it's very 'hacky'. Please fix this.
I'm using GatsbyJS version 2.23.12.
Update: While this is being patched you can add the following to your gatsby-node.js file (if you're using Gatsby). If you aren't using Gatsby, you can use https://github.com/gregberge/loadable-components
exports.onCreateWebpackConfig = ({ stage, actions, loaders }) => {
if (stage === 'build-html') {
actions.setWebpackConfig({
module: {
rules: [
{
test: /react-editor-js/,
use: loaders.null(),
},
{
test: /@editorjs/,
use: loaders.null(),
},
],
},
});
}
};
You can read more about this code here: https://www.gatsbyjs.org/docs/debugging-html-builds/#fixing-third-party-modules
I've been using NextJS and SSR with Editor JS and this works for me
import React, { useEffect, useRef, useState } from 'react';
import EditorJs from 'react-editor-js';
import List from '@editorjs/list';
import Paragraph from '@editorjs/paragraph';
const EDITOR_JS_TOOLS = {
paragraph: {
class: Paragraph,
inlineToolbar: true,
},
list: {
class: List,
inlineToolbar: true,
},
};
export default ({
id,
label,
labelStyle,
editorContainerStyle,
getValue,
Data,
}) => {
const instanceRef = useRef(null);
async function handleSave() {
// const savedData = await
instanceRef.current
.save()
.then((outputData) => {
function replaceAll(str, find, replace) {
function escapeRegExp(string) {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
}
let stringData = JSON.stringify(outputData);
let formattedData = replaceAll(stringData,'<b>','<strong>');
formattedData = replaceAll(stringData, '<b/>', '<strong/>');
getValue(formattedData);
})
.catch((error) => {
console.log('Saving failed: ', error);
});
}
console.log(Data)
return (
<div>
<style global jsx>
{`
.ce-block__content {
max-width: 100%;
margin: 0 5rem;
}
.ce-toolbar__content {
max-width: 100%;
margin: 0 3.5rem;
position: relative;
}
.editor-style {
border-radius: 0.25rem;
border-color: #ced4da;
}
`}
</style>
<label
htmlFor={id}
className={
typeof labelStyle === 'undefined' || labelStyle === null
? `label-style`
: `label-style {labelStyle}`
}>
{label}
</label>
<div
className={`border rounded ${
typeof editorContainerStyle !== undefined
? editorContainerStyle
: null
}`}>
<EditorJs
holder={id}
instanceRef={(instance) => (instanceRef.current = instance)}
data={
typeof Data === 'string' && Data.length !== 0
? console.log(JSON.parse(Data))
: Data
}
onChange={(e) => handleSave(e)}
tools={EDITOR_JS_TOOLS}>
<div id={id} />
</EditorJs>
</div>
</div>
);
};
Hey @Moikapy,
Can you please share the code? I copied your above snippet in an index.js file for NextJS but got the below error -
Server Error
ReferenceError: window is not defined
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Thanks
Hi! everyone.
By default, editor-js does not support SSR. But i think if modern fornt-end library, should help users not to care.
Therefore, this issue not close differently from other related issues (#31, #1), and will be updated asap.
Thanks!
I use import loadable from '@loadable/component'
example :
import loadable from '@loadable/component'
const EditorJs = loadable(() => import('~/utils/editors'))
This is an open issue with @editorjs https://github.com/codex-team/editor.js/issues/1036
NextJS has a function to ensure that React components work properly with dynamic imports. Something like this should work in theory:
import dynamic from "next/dynamic";
const EditorJs = dynamic(() => import("react-editor-js"));
// Use EditorJs as component
This should work:
const EditorJs = dynamic(() => import("react-editor-js"), {
ssr: false,
loading: () => <p>loading editor.js ...</p>,
});
NextJS has a function to ensure that React components work properly with dynamic imports. Something like this should work in theory:
import dynamic from "next/dynamic"; const EditorJs = dynamic(() => import("react-editor-js")); // Use EditorJs as component
This will work @nithinkashyapn
Hi guys, question, editor is ok now, but, when i try to load a plugin, app crashes, asking for "window" again
Make editor in another component and import it in your main page as a dynamic component
this is working fine while ssr and without any type error because importing react-editor-js as a dynamic component it loses its type definitions
eg: my Editor component ( RTE.tsx )
import EditorJs from "react-editor-js";
import { EDITOR_JS_TOOLS } from "./constants";
export default function RTE_Component() {
return (
<EditorJs
tools={EDITOR_JS_TOOLS}
data={[
time: 1556098174501,
blocks: [{ type: "header", data: { text: "Guides", level: 2 } }],
version: "2.12.4",
]}
inlineToolbar={true}
hideToolbar={true}
/>
);
}
my index.tsx
import dynamic from "next/dynamic";
import Head from "next/head";
const RTE_Component = dynamic(() => import("components/RTE"), { ssr: false });
export default function Home() {
return (
<>
<Head>Localhost - Home</Head>
<form action="http://localhost:3000/api/post" method="post">
<RTE_Component />
</form>
</>
);
}
@bilordigor the solution by @zakiAzfar helps with the plugins as well: plugins have to be imported and configured in the component imported dynamically with ssr disabled.
Using the require statement in the useEffect hook solved this issue for me in Nextjs. Thanks
import {useEffect} from 'react'
export default function editor() {
useEffect(() => {
const Editorjs = require('@editorjs/editorjs')
new Editorjs('editorjs')
}, [])
return(
<div id='editorjs'></div>
)
}
Make the editorJS a child component, then you just need to import dynamically in your pages.
I am also running into issues in running nextJs with this editorComponent. I am running :
import dynamic from "next/dynamic";
const EditorJs = dynamic(() => import("react-editor-js"));
// Use EditorJs as component
and it should work, but it doesn't which is weird ...
RE:EDIT:
Actually it works now. I have downgraded to version 1.9.0. I was running in version 2. So maybe there is something wrong with version 2. Error is something like
Check your code at Editor.js:11. at Editor at LoadableImpl
This worked for me using the newest version.
Editor.tsx
import { useCallback, useRef } from "react";
import { createReactEditorJS } from "react-editor-js";
const Editor = () => {
const ReactEditorJS = createReactEditorJS();
const editorJS = useRef(null);
const handleInitialize = useCallback((instance) => {
editorJS.current = instance;
}, []);
const handleSave = useCallback(async () => {
const savedData = await editorJS.current.save();
}, []);
return <ReactEditorJS onInitialize={handleInitialize} />;
};
export default Editor;
index.tsx
import React from "react";
import dynamic from "next/dynamic";
const EditorJs = dynamic(() => import("./Editor"), {
ssr: false,
});
const index = () => {
return <EditorJs />;
};
export default index;
If I need initial data on ssr ?
react-editor-js is support SSR partially.
const ReactEditorJS = createReactEditorJS();
<ReactEditorJS holder="ssrHolder" /> // works fine!
const tools = dynamic(() => import("./tools"), { ssr: false });
How to do dynamic import in nextjs 13 beta version? What would be the equivalent code in beta next 13 js? // let CustomEditor = dynamic(() => import("../../components/tools"), { // ssr: false, // }); .....
https://github.com/codex-team/editor.js/discussions/2223#discussion-4674964
const tools = dynamic(() => import("./tools"), { ssr: false });
Anyone tried rendering multiple holders with dynamic divs/holder ids? - If I disable SSR I get TypeError:EditorJS is not a constructor.
@Chiranjeev-droid Wackiest solution, couldn't think of anything else though.
If anyone else knows better approach for Next 13-14, please share and tag me
"use client";
import { createReactEditorJS } from "react-editor-js";
import { EDITOR_JS_TOOLS } from "./tools";
import { useEffect } from "react";
import ReactDOM from "react-dom";
const ReactEditorJS = createReactEditorJS();
function Editor({ blocks }: any) {
useEffect(() => {
const holder = document.getElementById("holder");
if (!holder) return;
const Editor = (
<ReactEditorJS
tools={EDITOR_JS_TOOLS}
defaultValue={blocks}
placeholder={`Write from here...`}
minHeight={900}
inlineToolbar={true}
/>
);
const portalElement = ReactDOM.createPortal(Editor, holder);
// @ts-ignore
ReactDOM.createRoot(holder).render(portalElement);
}, [blocks]);
return <></>;
}
export default Editor;
P.S. Don't forget to set position of holder to relative