React Hook Wrapper for Quill.
SSR Safe • Typescript Support • Unopinionated • No Dependencies • Tiny Package Size
// Install packages
$ pnpm add react-quilljs quill
or
$ yarn add react-quilljs quill
or
$ npm install react-quilljs quill
// If you are using Typescript
$ pnpm add -D @types/quill
Code Sandbox Playground Example
import React from 'react';
import { useQuill } from 'react-quilljs';
// or const { useQuill } = require('react-quilljs');
import 'quill/dist/quill.snow.css'; // Add css for snow theme
// or import 'quill/dist/quill.bubble.css'; // Add css for bubble theme
export default () => {
const { quill, quillRef } = useQuill();
console.log(quill); // undefined > Quill Object
console.log(quillRef); // { current: undefined } > { current: Quill Editor Reference }
return (
<div style={{ width: 500, height: 300 }}>
<div ref={quillRef} />
</div>
);
};
export default () => {
const { quill, quillRef } = useQuill();
React.useEffect(() => {
if (quill) {
quill.clipboard.dangerouslyPasteHTML('<h1>React Hook for Quill!</h1>');
}
}, [quill]);
return (
<div style={{ width: 500, height: 300 }}>
<div ref={quillRef} />
</div>
);
};
export default () => {
const { quill, quillRef } = useQuill();
React.useEffect(() => {
if (quill) {
quill.on('text-change', (delta, oldDelta, source) => {
console.log('Text change!');
console.log(quill.getText()); // Get text only
console.log(quill.getContents()); // Get delta contents
console.log(quill.root.innerHTML); // Get innerHTML using quill
console.log(quillRef.current.firstChild.innerHTML); // Get innerHTML using quillRef
});
}
}, [quill]);
return (
<div style={{ width: 500, height: 300 }}>
<div ref={quillRef} />
</div>
);
};
export default () => {
const counterRef = React.useRef();
const { quill, quillRef, Quill } = useQuill({ modules: { counter: true } });
if (Quill && !quill) {
// For execute this line only once.
Quill.register('modules/counter', function(quill, options) {
quill.on('text-change', function() {
const text = quill.getText();
// There are a couple issues with counting words
// this way but we'll fix these later
counterRef.current.innerText = text.split(/\s+/).length;
});
});
}
return (
<div style={{ width: 500, height: 300 }}>
<div ref={quillRef} />
<div ref={counterRef} />
</div>
);
};
export default () => {
const { quill, quillRef, Quill } = useQuill({ modules: { magicUrl: true }});
if (Quill && !quill) { // For execute this line only once.
const MagicUrl = require('quill-magic-url').default; // Install with 'yarn add quill-magic-url'
Quill.register('modules/magicUrl', MagicUrl);
}
return (
<div style={{ width: 500, height: 300 }}>
<div ref={quillRef} />
</div>
);
};
import 'quill/dist/quill.snow.css'; // Add css for snow theme
// import 'quill/dist/quill.bubble.css'; // Add css for bubble theme
export default () => {
const theme = 'snow';
// const theme = 'bubble';
const modules = {
toolbar: [
['bold', 'italic', 'underline', 'strike'],
],
};
const placeholder = 'Compose an epic...';
const formats = ['bold', 'italic', 'underline', 'strike'];
const { quillRef } = useQuill({ theme, modules, formats, placeholder });
return (
<div style={{ width: 500, height: 300, border: '1px solid lightgray' }}>
<div ref={quillRef} />
</div>
);
};
export default () => {
const { quillRef } = useQuill({
modules: {
toolbar: '#toolbar'
},
formats: ["size", "bold", "script"], // Important
});
return (
<div style={{ width: 500, height: 300 }}>
<div ref={quillRef} />
<div id="toolbar">
<select className="ql-size">
<option value="small" />
<option selected />
<option value="large" />
<option value="huge" />
</select>
<button className="ql-bold" />
<button className="ql-script" value="sub" />
<button className="ql-script" value="super" />
</div>
<div id="editor" />
</div>
);
};
import fetch from 'isomorphic-unfetch';
export default () => {
const { quill, quillRef } = useQuill();
// Insert Image(selected by user) to quill
const insertToEditor = (url) => {
const range = quill.getSelection();
quill.insertEmbed(range.index, 'image', url);
};
// Upload Image to Image Server such as AWS S3, Cloudinary, Cloud Storage, etc..
const saveToServer = async (file) => {
const body = new FormData();
body.append('file', file);
const res = await fetch('Your Image Server URL', { method: 'POST', body });
insertToEditor(res.uploadedImageUrl);
};
// Open Dialog to select Image File
const selectLocalImage = () => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.onchange = () => {
const file = input.files[0];
saveToServer(file);
};
};
React.useEffect(() => {
if (quill) {
// Add custom handler for Image Upload
quill.getModule('toolbar').addHandler('image', selectLocalImage);
}
}, [quill]);
return (
<div style={{ width: 500, height: 300, border: '1px solid lightgray' }}>
<div ref={quillRef} />
</div>
);
};
Options for Quill Configuration.
Type: Object
-
theme
Quill Theme.
Type:String
Default:'snow'
-
modules
Quill Modules.
Type:Object
Default:{ toolbar: [ ['bold', 'italic', 'underline', 'strike'], [{ align: [] }], [{ list: 'ordered'}, { list: 'bullet' }], [{ indent: '-1'}, { indent: '+1' }], [{ size: ['small', false, 'large', 'huge'] }], [{ header: [1, 2, 3, 4, 5, 6, false] }], ['link', 'image', 'video'], [{ color: [] }, { background: [] }], ], clipboard: { matchVisual: false, }, }
-
formats
Quill Formats.
Type:Array
Default:[ 'bold', 'italic', 'underline', 'strike', 'align', 'list', 'indent', 'size', 'header', 'link', 'image', 'video', 'color', 'background', ]
Quill object.
You can use quill apis(https://quilljs.com/docs/api/) with this object.
Type: Object
Quill Editor reference.
Type: RefObject
Quill class. You can use register, find, import, debug.
Please refer example above.
Type: Class
- React Checklist - Make Checkbox List Easy and Simple with React Hooks.
- React Store - React Hook Store with useContext and useReducer for State Management.
- Decode URI Component Charset - Decode URI Component with Charset such as 'euc-kr' without tears.
MIT