Quill
Allow users to create rich text content with the Quill editor.
Getting Started
Installation
To get started, you can install the package with the following command:
npm install @vueup/vue-quill@latest --save
Create CSS File
In order to make the editor match the design of this website (and the whole shadcn/ui theme), I had to add this css file: You should copy this and add it to your project.
@reference "./tailwind.css";
@import "@vueup/vue-quill/dist/vue-quill.snow.css";
@import "@vueup/vue-quill/dist/vue-quill.bubble.css";
@reference "./tailwind.css";
.ql-toolbar {
&.ql-snow {
@apply rounded-t-md border-border font-sans;
}
&.ql-snow {
.ql-stroke {
@apply stroke-muted-foreground;
}
.ql-fill {
@apply fill-muted-foreground;
}
button {
@apply mx-0.5 rounded hover:bg-muted hover:text-foreground;
&:hover {
.ql-fill {
@apply fill-foreground;
}
.ql-stroke {
@apply stroke-foreground;
}
}
.ql-stroke {
@apply stroke-muted-foreground;
}
&.ql-active {
@apply bg-primary text-primary-foreground;
.ql-stroke {
@apply stroke-primary-foreground;
}
.ql-fill {
@apply fill-primary-foreground;
}
}
}
.ql-formats {
svg,
.ql-picker-label,
.ql-picker {
@apply text-muted-foreground;
}
button {
@apply mx-0.5 rounded hover:bg-muted hover:text-foreground;
&:hover {
.ql-fill {
@apply fill-foreground;
}
.ql-stroke {
@apply stroke-foreground;
}
}
.ql-fill {
@apply fill-muted-foreground;
}
.ql-stroke {
@apply stroke-muted-foreground;
}
&.ql-active {
@apply bg-primary text-primary-foreground;
.ql-fill {
@apply fill-primary-foreground;
}
.ql-stroke {
@apply stroke-primary-foreground;
}
}
}
.ql-picker {
@apply rounded;
.ql-picker-options {
@apply mt-1 rounded border-border bg-card p-1;
.ql-picker-item {
@apply rounded hover:bg-muted hover:text-foreground;
&.ql-selected {
@apply bg-primary text-primary-foreground;
}
}
}
}
.ql-align,
.ql-color-picker {
&:hover {
.ql-fill {
@apply fill-foreground;
}
.ql-stroke {
@apply stroke-foreground;
}
}
.ql-picker-options {
@apply mt-1 rounded border-border bg-card p-1;
.ql-picker-item {
&.ql-selected {
@apply bg-primary text-primary-foreground;
.ql-stroke {
@apply stroke-primary-foreground;
}
}
}
}
}
.ql-picker-label {
@apply hover:rounded hover:bg-muted hover:text-foreground;
&.ql-active {
@apply rounded bg-primary text-primary-foreground;
.ql-stroke {
@apply stroke-primary-foreground;
}
}
}
.ql-stroke {
@apply stroke-muted-foreground;
}
.ql-expanded {
.ql-picker-label {
@apply rounded border-border;
}
}
}
}
}
.ql-container {
@apply min-h-[150px] bg-transparent font-sans text-sm focus-within:border-ring! focus-within:ring-[3px] focus-within:ring-ring/50 dark:bg-input/30;
&.ql-snow {
a {
@apply text-sky-500 hover:text-sky-500;
}
@apply rounded-b-md border-border;
}
.ql-editor {
@apply min-h-[150px];
.ql-font-monospace {
@apply font-mono;
}
&.ql-blank {
&:before {
@apply text-muted-foreground not-italic;
}
}
}
.ql-tooltip {
@apply z-[9999] rounded border-border bg-card px-4 py-2 text-sm text-card-foreground shadow before:cursor-pointer before:font-medium;
input[type="text"] {
@apply h-8 w-[200px] rounded border-border bg-muted/30 p-2 text-sm text-foreground focus:ring-1 focus:ring-ring focus:outline-none;
}
.ql-preview {
@apply text-sm leading-[26px] underline underline-offset-2;
}
.ql-remove {
@apply text-destructive hover:text-destructive;
}
}
}
.ql-container.ql-bubble {
@apply rounded-md border;
.ql-tooltip {
@apply z-[9999] rounded-lg border border-border bg-card px-4 py-2 text-sm text-card-foreground shadow before:cursor-pointer before:font-medium;
input[type="text"] {
@apply h-8 w-[200px] rounded border-border bg-muted/30 p-2 text-sm text-foreground focus:ring-1 focus:ring-ring focus:outline-none;
}
.ql-preview {
@apply text-sm leading-[26px] underline underline-offset-2;
}
.ql-remove {
@apply text-destructive hover:text-destructive;
}
&:not(.ql-flip) .ql-tooltip-arrow {
@apply border-b-border;
}
.ql-toolbar {
.ql-stroke {
@apply stroke-muted-foreground;
}
.ql-fill {
@apply fill-muted-foreground;
}
button {
@apply mx-0.5 rounded hover:bg-muted hover:text-foreground;
&:hover {
.ql-fill {
@apply fill-foreground;
}
.ql-stroke {
@apply stroke-foreground;
}
}
.ql-stroke {
@apply stroke-muted-foreground;
}
&.ql-active {
@apply bg-primary text-primary-foreground;
.ql-stroke {
@apply stroke-primary-foreground;
}
.ql-fill {
@apply fill-primary-foreground;
}
}
}
.ql-formats {
svg,
.ql-picker-label,
.ql-picker {
@apply text-muted-foreground;
}
button {
@apply mx-0.5 rounded hover:bg-muted hover:text-foreground;
&:hover {
.ql-fill {
@apply fill-foreground;
}
.ql-stroke {
@apply stroke-foreground;
}
}
.ql-fill {
@apply fill-muted-foreground;
}
.ql-stroke {
@apply stroke-muted-foreground;
}
&.ql-active {
@apply bg-primary text-primary-foreground;
.ql-fill {
@apply fill-primary-foreground;
}
.ql-stroke {
@apply stroke-primary-foreground;
}
}
}
.ql-picker {
@apply rounded;
.ql-picker-options {
@apply mt-1 rounded border border-border bg-card p-1;
.ql-picker-item {
@apply rounded px-1 hover:bg-muted hover:text-foreground;
&.ql-selected {
@apply bg-primary text-primary-foreground;
}
}
}
}
.ql-align,
.ql-color-picker {
&:hover {
.ql-fill {
@apply fill-foreground;
}
.ql-stroke {
@apply stroke-foreground;
}
}
.ql-picker-options {
@apply mt-1 rounded border border-border bg-card p-1;
.ql-picker-item {
&.ql-selected {
@apply bg-primary text-primary-foreground;
.ql-stroke {
@apply stroke-primary-foreground;
}
}
}
}
}
.ql-picker-label {
@apply hover:rounded hover:bg-muted hover:text-foreground;
&.ql-active {
@apply rounded bg-primary text-primary-foreground;
.ql-stroke {
@apply stroke-primary-foreground;
}
}
}
.ql-stroke {
@apply stroke-muted-foreground;
}
.ql-expanded {
.ql-picker-label {
@apply rounded border-border;
}
}
}
}
}
}
Usage
Basic
Here is a basic example of how to use the Quill component. We are using a technique called Slot Forwarding
so that if the developer wants to create a component and pass through the toolbar
slot, they can do so.
Toolbar
We can add our custom toolbar configuration by using the toolbar
prop.
Slot - Toolbar
Another way of customizing the toolbar is by using the toolbar
slot. This way, we can create a custom toolbar with our own components.
Bubble Theme
We can pass the bubble
value to the theme
prop to use the snow theme.
You have to select something in the editor to see the toolbar.
Module
We can pass an object or an array of objects to the module
prop to use any Quill module.
Something like this:
import BlotFormatter from "quill-blot-formatter";
type SingleModule = {
name: string;
module: any;
options?: any;
};
type ModuleObject = SingleModule | SingleModule[];
const modules: ModuleObject = {
name: "blotFormatter",
module: BlotFormatter,
options: {
/* options */
},
};
Upload an image to see the module in action.