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