Editor.js is an amazing free block-style editor. This editor works a lot like the Notion editor with each line treated as a block.
It outputs clean data in JSON-format instead of heavy HTML markup.
In this post, I will explain how you can set up Editor.js with Next.js 13 and use it as a text editor in your application.
In case you are new to Next.js, do check out my post on introduction to Next.js.
1 – Setting up a Next.js 13 Project
The best way to set-up a new project using Next.js 13 is by using create-next-app
.
Here’s the command you can use:
$ npx create-next-app@latest
During installation, you’ll see the below prompts and you can choose the default options.
What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like to use `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the default import alias? No / Yes
What import alias would you like configured? @/*
In the case of this example, I will be showing the JS version so I’ve selected ‘No’ for TypeScript. Also, we are using TailwindCSS for some basic styling.
After the prompts, create-next-app
will create a folder with your project name and install the required dependencies within the project.
2 – Installing Editor.js within the Next.js project
The next step is to set-up Editor.js for use within our Next.js project.
Now, Editor.js is structured in an interesting way. There is a core Editor.js library that you need to install with the below command.
$ npm install @editorjs/editorjs
However, each type of block supported by Editor.js is a separate plugin in itself. By default, only the Paragraph block is present in the core package.
For other types of blocks such as Headings, Code, Lists, Image and so on, you’ve to install separate packages.
Here’s a list of all the packages I’ve installed using npm install
.
"@editorjs/checklist": "^1.5.0",
"@editorjs/code": "^2.8.0",
"@editorjs/delimiter": "^1.3.0",
"@editorjs/editorjs": "^2.28.0",
"@editorjs/embed": "^2.5.3",
"@editorjs/header": "^2.7.0",
"@editorjs/image": "^2.8.1",
"@editorjs/inline-code": "^1.4.0",
"@editorjs/link": "^2.5.0",
"@editorjs/list": "^1.8.0",
"@editorjs/paragraph": "^2.10.0",
"@editorjs/quote": "^2.5.0",
"@editorjs/simple-image": "^1.5.1",
You can choose the block types depending on your requirements and install only those. Also, you can add more blocks in the future if needed. You can find the complete list of available blocks on this link.
Lastly, you also need to install one more package known as editorjs-html
. Basically, this package transforms the JSON-formatted block data to HTML for rendering purposes.
3 – Creating the Editor Component
Within our project’s src
directory, we will create a folder named components
.
Inside that folder, we will create a file named Editor.js
with the below code.
import React, { useEffect, useRef } from "react";
import EditorJS, { OutputData } from "@editorjs/editorjs";
import { EDITOR_TOOLS } from "./EditorTools";
export default function Editor({ data, onChange, holder }) {
//add a reference to editor
const ref = useRef();
//initialize editorjs
useEffect(() => {
//initialize editor if we don't have a reference
if (!ref.current) {
const editor = new EditorJS({
holder: holder,
tools: EDITOR_TOOLS,
data,
async onChange(api, event) {
const data = await api.saver.save();
onChange(data);
},
});
ref.current = editor;
}
//add a return function handle cleanup
return () => {
if (ref.current && ref.current.destroy) {
ref.current.destroy();
}
};
}, []);
return <div id={holder} className="prose max-w-full" />;
};
Basically, here we are initializing a new instance of EditorJS
.
The EditorJS instance takes as input the details of the various plugins we want to enable. This is provided as part of the tools
configuration object.
As you can see, we created a separate file named EditorTools
to maintain the data of the tools or plugins we want to add to the editor.
See below:
import CheckList from "@editorjs/checklist";
import Code from "@editorjs/code";
import Delimiter from "@editorjs/delimiter";
import Embed from "@editorjs/embed";
import Image from "@editorjs/image";
import InlineCode from "@editorjs/inline-code";
import Link from "@editorjs/link";
import List from "@editorjs/list";
import Quote from "@editorjs/quote";
import SimpleImage from "@editorjs/simple-image";
import Paragraph from "@editorjs/paragraph";
import Header from "@editorjs/header"
export const EDITOR_TOOLS = {
code: Code,
header: {
class: Header,
config: {
placeholder: 'Enter a Header',
levels: [2, 3, 4],
defaultLevel: 2
}
},
paragraph: Paragraph,
checklist: CheckList,
embed: Embed,
image: Image,
inlineCode: InlineCode,
link: Link,
list: List,
quote: Quote,
simpleImage: SimpleImage,
delimiter: Delimiter
};
This contains a list of all the Block types that we want to enable for our Editor. Depending on your requirement, you can add or remove a particular Block type.
4 – Using the Editor Component in Next.js Component
In Next.js 13, we now have the App router. Each project has an app
folder under the src
directory.
Within the app
folder, we will create a new folder named editor
. Inside it, create a file page.js
and place the below code within it:
"use client"
import dynamic from "next/dynamic";
import { useState } from "react";
import PreviewRenderer from "@/components/PreviewRenderer";
// important that we use dynamic loading here
// editorjs should only be rendered on the client side.
const Editor = dynamic(() => import("../../components/Editor"), {
ssr: false,
});
export default function EditorPage() {
//state to hold output data. we'll use this for rendering later
const [data, setData] = useState();
return (
<div className="grid grid-cols-2 gap-2 m-2">
<div className="col-span-1">
<h1>Editor</h1>
<div className="border rounded-md">
<Editor
data={data}
onChange={setData}
holder="editorjs-container"
/>
</div>
</div>
<div className="col-span-1">
<h1>Preview</h1>
<div className="border rounded-md">
<div className="p-16">{data && <PreviewRenderer data={data} />}</div>
</div>
</div>
</div>
);
};
What are we doing over here?
Basically, this is the page where we will be showing the editor. Since, we have placed it inside the editor
folder it will be available on http://localhost:3000/editor
route.
By default, all the pages in a Next.js app are rendered on the server. Therefore, we cannot use things like useState
within them. However, in this case we want to do so to handle the editor state and there the first line “use client” tells Next.js that this component will be rendered on the client.
Without this statement, you’ll get the below error when trying to access the page.
You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
Learn more: https://nextjs.org/docs/getting-started/react-essentials
Also, since we want to load the EditorJS instance on the client side, we use the dynamic loading for this component using next/dynamic
.
See the below code:
const Editor = dynamic(() => import("../../components/Editor"), {
ssr: false,
});
For testing purpose, we have also create a component that will preview what we type in the Block editor. This is the PreviewRenderer
component.
Here’s the code for the same:
const editorJsHtml = require("editorjs-html");
const EditorJsToHtml = editorJsHtml();
export default function PreviewRenderer ({ data }) {
const html = EditorJsToHtml.parse(data)
return (
<div className="prose max-w-full" key={data.time}>
{html.map((item, index) => {
if (typeof item === "string") {
return (
<div dangerouslySetInnerHTML={{ __html: item }} key={index}></div>
);
}
return item;
})}
</div>
);
};
Basically, here we are using the editorjs-html
package to convert the Block data to HTML markup.
5 – Testing our Editor
We can now test our editor.
Start the application dev server by running npm run dev
and visit http://localhost:3000/editor
.
You can test by entering some text in the Editor part and see it rendered in HTML on the Preview side.
Conclusion
As you can see Editor.js brings the power Block-type editing. We can use it to build various types of applications.
In this post, we saw how you can get started with Editor.js in Next.js 13 in a step-by-step manner.
If you have any comments or queries, please mention them in the comments section below.
0 Comments