Replacing Redactor with Quill in Knack — a better rich text editor
If you’ve ever been frustrated with Knack’s chosen editor Redactor’s paste behaviour, paragraph spacing, or limited toolbar control, here’s how to swap it out for Quill — a modern, open-source rich text editor that handles paste cleanly, renders consistently, and is much easier to customise.
What this gives you:
- Clean paste from Word, Excel, web pages — formatting is stripped to what Quill supports, no more
<p>tag chaos - Consistent line spacing between the editor and rendered detail views
- Proper bold, italic, underline, strikethrough that latch correctly when typing
- Font size, text colour and background colour pickers
- Easy toolbar customisation
- Works globally across all rich text fields in your app
JavaScript
/************************************************************************
QUILL RICH TEXT EDITOR - REPLACES REDACTOR
*************************************************************************/
(function() {
var link = document.createElement(‘link’);
link.rel = ‘stylesheet’;
link.href = ‘https://cdn.quilljs.com/1.3.7/quill.snow.css’;
document.head.appendChild(link);
var script = document.createElement(‘script’);
script.src = ‘https://cdn.quilljs.com/1.3.7/quill.min.js’;
script.onload = function() {
$(document).on(‘knack-view-render.any’, function() {
initQuillEditors();
});
};
document.head.appendChild(script);
})();
function initQuillEditors() {
$(‘.redactor-box’).each(function() {
var $box = $(this);
if ($box.data('quill-init')) return;
$box.data('quill-init', true);
var $textarea = $box.find('textarea');
var $redactorEditor = $box.find('.redactor-editor');
var existingContent = $textarea.val() || $redactorEditor.html() || '';
$redactorEditor.hide();
$box.find('.redactor-toolbar').hide();
var $quillContainer = $('<div class="quill-container"></div>');
$box.append($quillContainer);
var quill = new Quill($quillContainer[0], {
theme: 'snow',
modules: {
toolbar: [
['bold', 'italic', 'underline', 'strike'],
[{ list: 'ordered' }, { list: 'bullet' }],
[{ size: ['small', false, 'large', 'huge'] }],
[{ color: [] }, { background: [] }],
['clean']
]
}
});
if (existingContent) {
quill.clipboard.dangerouslyPasteHTML(existingContent);
}
quill.on('text-change', function() {
var html = $quillContainer.find('.ql-editor').html();
$textarea.val(html);
$textarea.trigger('change');
});
});
}
CSS:
/* Quill editor container */
.quill-container {
border: 1px solid #ccc;
border-radius: 4px;
}
/* Toolbar styling */
.ql-toolbar {
background: #eaecfa;
border-radius: 4px 4px 0 0;
padding: 2px !important;
}
.ql-toolbar button {
width: 24px !important;
height: 24px !important;
padding: 2px !important;
}
.ql-toolbar button svg {
width: 14px !important;
height: 14px !important;
}
.ql-toolbar .ql-picker {
font-size: 12px !important;
}
/* Editor area */
.ql-container {
font-size: 14px;
min-height: 120px;
}
.ql-editor {
min-height: 120px;
}
/* Shrink paragraph spacing in editor */
.ql-editor p {
margin: 0 !important;
line-height: 1.4 !important;
}
/* Shrink paragraph spacing in detail/rendered views */
.kn-detail-body p {
margin: 0 !important;
line-height: 1.4 !important;
}
/* Fix background colour span rendering in detail views to avoid line split */
.kn-detail-body span[style=“background-color”] {
display: inline !important;
}
Customising the toolbar
The toolbar is configured in the JavaScript as an array of arrays. Each inner array becomes a button group separated by a divider. Here are all the available options:
| Option | What it does |
|---|---|
'bold' |
Bold |
'italic' |
Italic |
'underline' |
Underline |
'strike' |
Strikethrough |
'link' |
Insert hyperlink |
'clean' |
Remove all formatting from selection |
{ list: 'ordered' } |
Numbered list |
{ list: 'bullet' } |
Bullet list |
{ indent: '-1' } |
Outdent |
{ indent: '+1' } |
Indent |
{ size: ['small', false, 'large', 'huge'] } |
Font size picker |
{ color: [] } |
Text colour picker |
{ background: [] } |
Background colour picker |
{ align: [] } |
Text alignment |
{ header: 1 } |
Heading 1 |
{ header: 2 } |
Heading 2 |
'blockquote' |
Block quote |
'code-block' |
Code block |
Example — minimal toolbar (bold, italic, underline, lists only):
toolbar: [
[‘bold’, ‘italic’, ‘underline’],
[{ list: ‘ordered’ }, { list: ‘bullet’ }],
[‘clean’]
]
Example — full featured toolbar:
toolbar: [
[‘bold’, ‘italic’, ‘underline’, ‘strike’],
[{ list: ‘ordered’ }, { list: ‘bullet’ }],
[{ indent: ‘-1’ }, { indent: ‘+1’ }],
[{ size: [‘small’, false, ‘large’, ‘huge’] }],
[{ color: [] }, { background: [] }],
[{ align: [] }],
[‘link’, ‘clean’]
]
Notes
- Applies to Knack CLASSIC only
- Content is saved as standard HTML to the Knack textarea, so existing Redactor content continues to render correctly
- If you have specific views where you want a larger editor (e.g. comment modals), scope the
min-heightto those view IDs - KTL compatibility — seems to work in apps using KTL, and Carig says he has been using this for some time so should be good to go.
- If you find issues or improvements, please share back in the thread
Enjoy, I hope this is of value to someone - it certainly fixed some headaches for me, especially with pasting content into Knack rick text fields.