Description
Quill Footnotes is a ready-made solution for React/Next.js projects, allowing you to insert and edit footnotes inside the Quill rich editor.
Installation
The Quill-footnotes module is published on the PrivJs.com registry. To access the module, please visit https://www.privjs.com/packages/quill-footnotes (opens in a new tab) and acquire the package. Following this, instructions will be sent to your email.
To configure the PrivJs access token, run the following command in your terminal:
npm login --registry https://r.privjs.com
Enter your email, username, and password when prompted. Afterward, you can install the package using the following command:
npm install quill-footnotes --registry https://r.privjs.com
This completes the installation process on your computer.
Note: The license key for your purchase can be found in your email.
Make sure you have react-quill
and quill
installed in your
React/NextJs app. If not, copy the command and run it in your
terminal:
npm i react-quill quill @types/quill
Getting Started
Import necessary modules from quill-footnotes:
import {
footnoteModules,
footnoteFormats,
onEditorContentChange,
} from "quill-footnotes";
Import necessary styles:
import "quill-footnotes/dist/styles/index.css";
FootnoteModules:
Modules allow you to customize quills default behaviour. FootnoteModules is a necessary object which contains instructions for a quill editor how to handle footnotes functionality.
Params:
ref
: React ref of typeReactQuill
toolbar id
: Your toolbar id in format#YOUR_TOOLBAR_ID
Usage:
In React components, Quill modules must be wrapped in the
useMemo
hook.
const modules = React.useMemo(() => {
return footnoteModules(ref, "#YOUR_TOOLBAR_ID");
}, []);
FootnoteFormats:
FootnoteFormats
is an array which contains necessary footnote
formats for your quill editor.
Usage:
You need to spread the footnoteFormats
array into your formats
array. For example:
const formats = ["header", "bold", ...YOUR_OTHER_FORMATS, ...footnoteFormats];
OnEditorContentChange:
OnEditorContentChange
is a function that should be called on every
editor change. This function implements logic for recalculating footnote positions.
Params
ref
: React ref of typeReactQuill
Usage:
You should add this function to your Quill's onChangeHandler
.
const handleChange = (newValue: string) => {
setValue(newValue);
onEditorContentChange(ref);
};
Note! Recommended to debounce the handleChange function.
Summary
Add necessary imports:
import ReactQuill from "react-quill";
import {
footnoteModules,
footnoteFormats,
onEditorContentChange,
} from "quill-footnotes";
import "quill-footnotes/dist/styles/index.css";
import "react-quill/dist/quill.snow.css";
Declare a formats
array with the formats you need and spread
footnoteFormats
into your formats
array:
const formats = ["header", "bold", ...YOUR_OTHER_FORMATS, ...footnoteFormats];
Inside your React component declare your editor state and a quill ref:
const [value, setValue] = React.useState("");
const ref = React.useRef<ReactQuill | null>(null);
Declare a handleChange
function:
const handleChange = (newValue: string) => {
setValue(newValue);
onEditorContentChange(ref);
};
Declare a modules
object:
const modules = React.useMemo(() => {
return footnoteModules(ref, "#YOUR_TOOLBAR_ID");
}, []);
Add a custom toolbar for your editor to your jsx:
<div id="YOUR_TOOLBAR_ID">
<span className="ql-formats">
<button className="ql-bold" />
<button className="ql-italic" />
<button className="ql-underline" />
</span>
<span className="ql-formats">
<button className="ql-link" />
<button className="ql-blockquote" />
<button className="ql-code" />
<button className="ql-image" />
</span>
<span className="ql-formats">
<button className="ql-list" value="ordered" />
<button className="ql-list" value="bullet" />
</span>
<span className="ql-formats">
<button className="ql-align" value="center" />
<button className="ql-align" value="right" />
<button className="ql-align" value="justify" />
</span>
<span className="ql-formats">
<button className="ql-insertFootnote">YOUR_INSERT_FOOTNOTE_ICON</button>
</span>
</div>
Add a reactQuill
to your jsx:
<ReactQuill
value={editorValue}
onChange={(newValue, delta, source) => {
if (source === "user") {
handleChange(newValue);
}
}}
modules={modules}
formats={formats}
ref={(el: ReactQuill) => {
ref.current = el;
}}
/>
NextJs Usage Note
Make sure you have added use client
at the top of your Quill
editor component. Then import the component into your parent
component where you want to use the editor using dynamic import
:
"use client";
import dynamic from "next/dynamic";
const Editor = dynamic(() => import("./components/Editor/Editor.js"), {
ssr: false,
});
HTML Usage
When adding a footnote, the corresponding footnote link gets the
data-ref
attribute with a value of the form #footnoteBottom-{FOOTNOTE_NUMBER}
. You may use this attribute to highlight the
corresponding footnote and scroll the page to its position. For
example:
React.useEffect(() => {
const footnotesArr: HTMLElement[] =
document.querySelectorAll("span.footnote");
footnotesArr.forEach((el: HTMLElement) =>
el.addEventListener("click", (e) => {
e.preventDefault();
const dataRef = el.getAttribute("data-ref");
const ftn = document.getElementById(dataRef.substring(1));
ftn.classList.remove("highlight");
void ftn.offsetWidth;
ftn.classList.add("highlight");
ftn.scrollIntoView({
behavior: "smooth",
block: "center",
});
})
);
}, [editorValue]);
And styles.css
:
.highlight {
animation-name: backgroundColorPalette;
animation-duration: 3s;
animation-iteration-count: 1;
animation-direction: alternate;
animation-timing-function: linear;
border-radius: 3px;
}
@keyframes backgroundColorPalette {
0% {
background: #b3e5fc;
}
100% {
background: transparent;
}
}