33
loading...
This website collects cookies to deliver better user experience
npx create-next-app next-editor-js-example
npm i @editorjs/editorjs @editorjs/delimiter @editorjs/embed @editorjs/header @editorjs/list @editorjs/marker @editorjs/paragraph
npm i react-editor-js
/blog/create
. Doing this is fairly simple in Next.js as we just need to add this path in the pages folder in our project root. While we are here, let's also set up other files. We also want a dynamic page for blogs, as we will also look at how to server-side render data output by editor.js. So now we also have, /blog/[slug]
in pages. Finally, create a components
folder in root, in there add two files, Editor/Editor.js
, Editor/EditorConstants.js
. With this, the final folder structure of our project will look like this.EditorConstants.js
file, import all the plugins you are using, and export them as one object.import Embed from '@editorjs/embed';
import Header from '@editorjs/header';
import Delimiter from '@editorjs/delimiter';
import List from '@editorjs/list';
import Marker from '@editorjs/marker';
const constants = {
embed: Embed,
list: List,
marker: Marker,
header: Header,
delimiter: Delimiter,
};
export default constants;
Editor.js
file(our custom editor component). Since editor.js doesn’t work with SSR, we need to find a workaround here which imports editor.js only once the code is running on the client-side. Next.js gives us an elegant way to solve this through dynamic imports and specifying the {ssr: false} option. Using this, we can dynamically import the react-editor-js
package. But we also have the plugins, which are also to be included on the client-side. I tried to do this in many ways, but if we want to import all the plugins at once using the EditorConstants.js
module that we created, I found the most effective way to do it is using a useEffect hook to dynamically import the plugins. The useEffect ensures that the module is imported only on the client-side.onSaveHandler
which receives the editor instance and gives us the data that we added in the editor. We can then have an onSave prop passed down from the parent that triggers a function in the parent and provides the content of the blog. The example below will make this clear.import { useEffect, useState } from 'react';
import Head from 'next/head';
import dynamic from 'next/dynamic';
const EditorJs = dynamic(() => import('react-editor-js'), { ssr: false });
let editorInstance;
const Editor = (props) => {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [editorTools, setEditorTools] = useState();
const onSaveHandler = async (editorInstance) => {
try {
const blogData = await editorInstance.save();
if (!title || title === '')
throw new Error('Title cannot be empty. Please enter title');
if (!blogData.blocks[0])
throw new Error('Blog cannot be empty. Please enter some data');
props.onSave(blogData, title, description);
} catch (err) {
console.log(err);
}
};
let editorComponent;
if (!editorTools) editorComponent = 'Loading...';
else {
editorComponent = (
<EditorJs
instanceRef={(instance) => (editorInstance = instance)}
tools={editorTools}
placeholder={`Let's write an awesome blog!`}
/>
);
}
useEffect(() => {
const importConstants = async () => {
const tools = (await import('../../components/Editor/EditorConstants'))
.default;
setEditorTools(tools);
};
importConstants();
}, []);
const inputStyle = {
maxWidth: '500px',
marginBottom: '20px',
height: '30px',
};
return (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<Head>
<title>Create Blog</title>
<meta name='description' content='Generated by create next app' />
</Head>
<input
style={inputStyle}
placeholder='Your Blog Title'
value={title}
onChange={(event) => setTitle(event.target.value)}
/>
<input
style={inputStyle}
placeholder='Your Blog Description'
value={description}
onChange={(event) => setDescription(event.target.value)}
/>
{editorComponent}
<div style={{ textAlign: 'center' }}>
<button onClick={() => onSaveHandler(editorInstance)}>Save</button>
</div>
</div>
);
};
export default Editor;
import Head from 'next/head';
import Editor from '../../components/Editor/Editor';
const CreateBlog = (props) => {
const onSaveHandler = async (blogData, title, description) => {
const toSaveData = {
title,
blogData,
description,
};
console.log(toSaveData);
//make your ajax call to send the data to your server and save it in a database
};
return (
<div style={{ width: '80%', margin: '0 auto' }}>
<Head>
<title>Create new blog</title>
</Head>
<h1>Create Blog</h1>
<Editor
onSave={(editorData, title, description) =>
onSaveHandler(editorData, title, description)
}
/>
</div>
);
};
export default CreateBlog;
npm i editorjs-react-renderer
/pages/blog/[slug]
and pass on the blog data created through editor.js.import Output from 'editorjs-react-renderer';
const BlogDetail = (props) => {
const { data, error } = props;
if (error) {
console.log(error);
return null;
}
return (
<div style={{ width: '80%', margin: '0 auto' }}>
<h1>{data.title}</h1>
<div style={{ marginBottom: '3rem' }}>{data.description}</div>
<div style={{ maxWidth: '800px', margin: '0 auto' }}>
<Output data={data.blogData} />
</div>
</div>
);
};
export default BlogDetail;
export async function getServerSideProps({ query }) {
const { slug } = query;
//make an ajax call to get your blog
return {
props: {
data: {
//return your blog data saved through editor.js
},
},
};
}
export default BlogDetail;