DEV Community

Cover image for Integrating React QuillJs with React Hook Form
Arnaud F.
Arnaud F.

Posted on

1

Integrating React QuillJs with React Hook Form

Quilljs is a free, open-source library that lets developers easily add rich text editing capabilities to their web applications. It provides a familiar WYSIWYG (What You See Is What You Get) editing experience, similar to popular word processors, allowing users to format text, add images, and create interactive content. It's also known for being customizable, so developers can tailor it to their specific needs.

While React Quill was once a popular option for adding rich text editing to React applications, there are a couple reasons why it might not be the most relevant choice these days:

  • Out of Date: React Quill hasn't had major updates in over two years. This raises concerns about compatibility with newer React versions and security risks.
  • Quill v2 Issues: There's a new version of Quill (v2), but React Quill doesn't seem to work with it smoothly, potentially causing problems.
  • Outdated Techniques: React Quill might use older methods for handling the web page structure (DOM manipulation) that aren't ideal for modern React development.

There's a new React library called React Quilljs that's actively maintained and integrates well with React projects.

I'll show you how to use React Quilljs with React Hook Form in a TypeScript project. While it might not be the ultimate solution, it works!

Install dependencies

npm install react-quilljs quill
npm install -D @types/quill
npm install react-hook-form
Enter fullscreen mode Exit fullscreen mode

For other configuration options, check out the react-quilljs documentation.

Usage

First, create a file named App.tsx in the root directory. This file will be used to render the editor. Be caution when using this file like this, because it will not work without the Layout.tsx component (see the project on GitHub)

import React, { useState } from "react";
import { Controller, useForm, SubmitHandler } from "react-hook-form";
import { Layout } from "./components/Layout";
import { Editor } from "./components/Editor";
interface IFormInput {
title: string;
content: string;
}
export default function App() {
const [content, setContent] = useState("");
const [output, setOutput] = useState("");
const {
control,
register,
handleSubmit,
formState: { errors },
} = useForm<IFormInput>();
const onSubmit: SubmitHandler<IFormInput> = (data) => {
const newPost = { title: data.title, content: content };
setOutput(JSON.stringify(newPost, undefined, 2));
};
return (
<Layout>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="space-y-12">
<div className="pb-12">
<h2 className="text-base font-semibold leading-7 text-gray-900">
New post
</h2>
<p className="mt-1 text-sm leading-6 text-gray-600">
Add new post to your blog.
</p>
<div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
<div className="sm:col-span-4">
<label
htmlFor="title"
className="block text-sm font-medium leading-6 text-gray-900"
>
Title *
</label>
<div className="mt-2">
<div className="flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-indigo-600 sm:max-w-md">
<input
type="text"
className="block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
placeholder="Enter post title"
{...register("title", { required: true })}
/>
</div>
{errors.title && (
<p className="mt-2 text-red-500 text-sm">
This field is required
</p>
)}
</div>
</div>
<div className="col-span-full">
<label
htmlFor="about"
className="block text-sm font-medium leading-6 text-gray-900"
>
Content
</label>
<div className="mt-2">
<Controller
name="content"
control={control}
render={({ field: { value } }) => (
<Editor
onChange={setContent}
value={value}
placeholder="Write something..."
/>
)}
/>
</div>
</div>
</div>
</div>
</div>
<div className="mt-3 col-span-full">
<textarea
readOnly
rows={5}
className="block w-full rounded-md border-0 py-1.5 pl-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
value={output}
placeholder="Click on the Save button to view results..."
/>
</div>
<div className="mt-6 flex items-center justify-end gap-x-6">
<button
type="button"
className="text-sm font-semibold leading-6 text-gray-900"
>
Cancel
</button>
<button
type="submit"
className="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
Save
</button>
</div>
</form>
</Layout>
);
}
view raw App.tsx hosted with ❤ by GitHub

Then, create a file named Editor.tsx in the components directory. This file will be used to render the editor.

import React from "react";
import { useQuill } from "react-quilljs";
import "quill/dist/quill.snow.css";
import "../styles/quill.editor.css";
interface EditorProps {
value: string;
placeholder: string;
onChange: (value: string) => void;
}
export const Editor = ({ value, placeholder, onChange }: EditorProps) => {
const theme = "snow";
const modules = {
toolbar: [
["bold", "italic", "underline", "strike"],
[{ align: [] }],
[{ list: "ordered" }, { list: "bullet" }],
[{ indent: "-1" }, { indent: "+1" }],
[{ header: [1, 2, 3, 4, 5, 6, false] }],
["link"],
[{ color: [] }],
],
};
const formats = [
"bold",
"italic",
"underline",
"strike",
"align",
"list",
"indent",
"header",
"link",
"color",
];
const { quill, quillRef } = useQuill({
theme,
modules,
formats,
placeholder,
});
React.useEffect(() => {
if (quill) {
if (value) {
quill.clipboard.dangerouslyPasteHTML(value);
}
quill.on("text-change", () => {
onChange(quill.root.innerHTML);
});
}
}, [quill, value, onChange]);
return (
<div style={{ height: 200 }}>
<div ref={quillRef} />
</div>
);
};
view raw Editor.tsx hosted with ❤ by GitHub

Conclusion

You can view results on CodeSandbox

codesandbox

The project is hosted on GitHub

Thank you

Life's too short for boring endings.

Top comments (0)

11 Tips That Make You a Better Typescript Programmer

typescript

1 Think in {Set}

Type is an everyday concept to programmers, but it’s surprisingly difficult to define it succinctly. I find it helpful to use Set as a conceptual model instead.

#2 Understand declared type and narrowed type

One extremely powerful typescript feature is automatic type narrowing based on control flow. This means a variable has two types associated with it at any specific point of code location: a declaration type and a narrowed type.

#3 Use discriminated union instead of optional fields

...

Read the whole post now!