Tailwind CSS WYSIWYG Text Editor - Flowbite
Use the wysiwyg text editor component from Flowbite to create and modify content by manipulating paragraphs, headings, images and styling them using all available options
The WYSIWYG text editor from Flowbite is open-source under the MIT license based on the Tip Tap library and allows you to easily edit complex text data with typography styles, links, images, videos, and more.
The markup and styles provided by Flowbite are all built with the utility classes from Tailwind CSS and the styles for the content inside the WYSIWYG text editor are based on the Flowbite Typography plugin.
All examples provided on this page have support for dark mode, RTL (right-to-left) styles, responsiveness on mobile devices and you can easily add your own functionality using JavaScript and the Flowbite API.
Getting started #
Before continuing make sure that you have Tailwind CSS, Flowbite, and Tip Tap installed in your project.
-
Follow the quickstart guide from Flowbite to enable the interactive elements
-
Install the Flowbite Typography plugin to format the content of text inside the WYSYIWYG editor preview:
- Terminal
npm i flowbite-typography
- Import the
flowbite-typographyplugin inside your main Tailwind CSS file:
- wysiwyg.js
@plugin "flowbite-typography";
Alternatively you can do the same but in your tailwind.config.js file:
- wysiwyg.js
// import the tailwind.config.js file in your main CSS file if using Tailwind CSS v4
module.exports = {
theme: {
// ...
},
plugins: [
require('flowbite-typography'),
// ...
],
}
- Finally, install Tip Tap either via NPM or skip this step if you’re using CDN:
- Terminal
npm install @tiptap/core @tiptap/pm @tiptap/starter-kit
Now you’re ready to use the examples below by copying the HTML markup and the JavaScript code.
Default text editor #
Use this example of a WYSIWYG text editor to enable basic typography styling and formatting, adding lists, links, images, videos, code blocks, aligning text, blockquotes, setting headers and paragraphs and more.
<div class="w-full bg-neutral-secondary-medium border border-default-medium rounded-base">
<div class="p-2 border-b border-default-medium">
<div class="flex flex-wrap items-center">
<div class="flex items-center space-x-1 rtl:space-x-reverse flex-wrap">
<button id="toggleBoldButton" data-tooltip-target="tooltip-bold" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5h4.5a3.5 3.5 0 1 1 0 7H8m0-7v7m0-7H6m2 7h6.5a3.5 3.5 0 1 1 0 7H8m0-7v7m0 0H6"/></svg>
<span class="sr-only">Bold</span>
</button>
<div id="tooltip-bold" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle bold
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleItalicButton" data-tooltip-target="tooltip-italic" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m8.874 19 6.143-14M6 19h6.33m-.66-14H18"/></svg>
<span class="sr-only">Italic</span>
</button>
<div id="tooltip-italic" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle italic
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleUnderlineButton" data-tooltip-target="tooltip-underline" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M6 19h12M8 5v9a4 4 0 0 0 8 0V5M6 5h4m4 0h4"/></svg>
<span class="sr-only">Underline</span>
</button>
<div id="tooltip-underline" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle underline
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleStrikeButton" data-tooltip-target="tooltip-strike" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 6.2V5h12v1.2M7 19h6m.2-14-1.677 6.523M9.6 19l1.029-4M5 5l6.523 6.523M19 19l-7.477-7.477"/></svg>
<span class="sr-only">Strike</span>
</button>
<div id="tooltip-strike" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle strike
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleHighlightButton" data-tooltip-target="tooltip-highlight" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M9 20H5.5c-.27614 0-.5-.2239-.5-.5v-3c0-.2761.22386-.5.5-.5h13c.2761 0 .5.2239.5.5v3c0 .2761-.2239.5-.5.5H18m-6-1 1.42 1.8933c.04.0534.12.0534.16 0L15 19m-7-6 3.9072-9.76789c.0335-.08381.1521-.08381.1856 0L16 13m-8 0H7m1 0h1.5m6.5 0h-1.5m1.5 0h1m-7-3.00001h4"/></svg>
<span class="sr-only">Highlight</span>
</button>
<div id="tooltip-highlight" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle highlight
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleCodeButton" type="button" data-tooltip-target="tooltip-code" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m8 8-4 4 4 4m8 0 4-4-4-4m-2-3-4 14"/></svg>
<span class="sr-only">Code</span>
</button>
<div id="tooltip-code" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Format code
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleLinkButton" data-tooltip-target="tooltip-link" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.213 9.787a3.391 3.391 0 0 0-4.795 0l-3.425 3.426a3.39 3.39 0 0 0 4.795 4.794l.321-.304m-.321-4.49a3.39 3.39 0 0 0 4.795 0l3.424-3.426a3.39 3.39 0 0 0-4.794-4.795l-1.028.961"/></svg>
<span class="sr-only">Link</span>
</button>
<div id="tooltip-link" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Add link
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="removeLinkButton" data-tooltip-target="tooltip-remove-link" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M13.2131 9.78732c-.6359-.63557-1.4983-.99259-2.3974-.99259-.89911 0-1.76143.35702-2.39741.99259l-3.4253 3.42528C4.35719 13.8485 4 14.7108 4 15.61c0 .8992.35719 1.7616.99299 2.3974.63598.6356 1.4983.9926 2.39742.9926.89912 0 1.76144-.357 2.39742-.9926l.32157-.3043m-.32157-4.4905c.63587.6358 1.49827.993 2.39747.993.8991 0 1.7615-.3572 2.3974-.993l3.4243-3.42528c.6358-.63585.993-1.49822.993-2.39741 0-.89919-.3572-1.76156-.993-2.39741C17.3712 4.357 16.509 4 15.6101 4c-.899 0-1.7612.357-2.397.9925l-1.0278.96062m7.3873 14.04678-1.7862-1.7862m0 0L16 16.4274m1.7864 1.7863 1.7862-1.7863m-1.7862 1.7863L16 20"/></svg>
<span class="sr-only">Remove link</span>
</button>
<div id="tooltip-remove-link" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Remove link
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleTextSizeButton" data-dropdown-toggle="textSizeDropdown" type="button" data-tooltip-target="tooltip-text-size" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 6.2V5h11v1.2M8 5v14m-3 0h6m2-6.8V11h8v1.2M17 11v8m-1.5 0h3"/></svg>
<span class="sr-only">Text size</span>
</button>
<div id="tooltip-text-size" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Text size
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<div id="textSizeDropdown" class="z-10 hidden bg-neutral-primary-medium border border-default-medium rounded-base shadow-lg w-72">
<ul class="p-2 space-y-1 text-sm text-body font-medium" aria-labelledby="toggleTextSizeButton">
<li>
<button data-text-size="16px" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">16px (Default)
</button>
</li>
<li>
<button data-text-size="12px" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-xs">12px (Tiny)
</button>
</li>
<li>
<button data-text-size="14px" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm">14px (Small)
</button>
</li>
<li>
<button data-text-size="18px" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-lg">18px (Lead)
</button>
</li>
<li>
<button data-text-size="24px" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-2xl">24px (Large)
</button>
</li>
<li>
<button data-text-size="36px" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-4xl">36px (Huge)
</button>
</li>
</ul>
</div>
<button id="toggleTextColorButton" data-dropdown-toggle="textColorDropdown" type="button" data-tooltip-target="tooltip-text-color" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="m6.08169 15.9817 1.57292-4m-1.57292 4h-1.1m1.1 0h1.65m-.07708-4 2.72499-6.92967c.0368-.09379.1673-.09379.2042 0l2.725 6.92967m-5.65419 0h-.00607m.00607 0h5.65419m0 0 .6169 1.569m5.1104 4.453c0 1.1025-.8543 1.9963-1.908 1.9963s-1.908-.8938-1.908-1.9963c0-1.1026 1.908-4.1275 1.908-4.1275s1.908 3.0249 1.908 4.1275Z"/></svg>
<span class="sr-only">Text color</span>
</button>
<div id="tooltip-text-color" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Text color
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<div id="textColorDropdown" class="z-10 hidden w-48 rounded-sm bg-neutral-primary-soft p-2 shadow-sm">
<div class="grid grid-cols-6 gap-2 group mb-3 items-center p-1.5 rounded hover:bg-neutral-tertiary">
<input type="color" id="color" value="#e66465" class="border-default-medium
border bg-neutral-tertiary rounded p-px px-1 w-full h-8 col-span-3" />
<label for="color" class="text-body text-xs font-medium col-span-3 group-hover:text-heading">Pick a color</label>
</div>
<div class="grid grid-cols-6 gap-1 mb-3">
<button type="button" data-hex-color="#1A56DB" style="background-color: #1A56DB" class="w-6 h-6 rounded-md"><span class="sr-only">Blue</span></button>
<button type="button" data-hex-color="#0E9F6E" style="background-color: #0E9F6E" class="w-6 h-6 rounded-md"><span class="sr-only">Green</span></button>
<button type="button" data-hex-color="#FACA15" style="background-color: #FACA15" class="w-6 h-6 rounded-md"><span class="sr-only">Yellow</span></button>
<button type="button" data-hex-color="#F05252" style="background-color: #F05252" class="w-6 h-6 rounded-md"><span class="sr-only">Red</span></button>
<button type="button" data-hex-color="#FF8A4C" style="background-color: #FF8A4C" class="w-6 h-6 rounded-md"><span class="sr-only">Orange</span></button>
<button type="button" data-hex-color="#0694A2" style="background-color: #0694A2" class="w-6 h-6 rounded-md"><span class="sr-only">Teal</span></button>
<button type="button" data-hex-color="#B4C6FC" style="background-color: #B4C6FC" class="w-6 h-6 rounded-md"><span class="sr-only">Light indigo</span></button>
<button type="button" data-hex-color="#8DA2FB" style="background-color: #8DA2FB" class="w-6 h-6 rounded-md"><span class="sr-only">Indigo</span></button>
<button type="button" data-hex-color="#5145CD" style="background-color: #5145CD" class="w-6 h-6 rounded-md"><span class="sr-only">Purple</span></button>
<button type="button" data-hex-color="#771D1D" style="background-color: #771D1D" class="w-6 h-6 rounded-md"><span class="sr-only">Brown</span></button>
<button type="button" data-hex-color="#FCD9BD" style="background-color: #FCD9BD" class="w-6 h-6 rounded-md"><span class="sr-only">Light orange</span></button>
<button type="button" data-hex-color="#99154B" style="background-color: #99154B" class="w-6 h-6 rounded-md"><span class="sr-only">Bordo</span></button>
<button type="button" data-hex-color="#7E3AF2" style="background-color: #7E3AF2" class="w-6 h-6 rounded-md"><span class="sr-only">Dark Purple</span></button>
<button type="button" data-hex-color="#CABFFD" style="background-color: #CABFFD" class="w-6 h-6 rounded-md"><span class="sr-only">Light</span></button>
<button type="button" data-hex-color="#D61F69" style="background-color: #D61F69" class="w-6 h-6 rounded-md"><span class="sr-only">Dark Pink</span></button>
<button type="button" data-hex-color="#F8B4D9" style="background-color: #F8B4D9" class="w-6 h-6 rounded-md"><span class="sr-only">Pink</span></button>
<button type="button" data-hex-color="#F6C196" style="background-color: #F6C196" class="w-6 h-6 rounded-md"><span class="sr-only">Cream</span></button>
<button type="button" data-hex-color="#A4CAFE" style="background-color: #A4CAFE" class="w-6 h-6 rounded-md"><span class="sr-only">Light Blue</span></button>
<button type="button" data-hex-color="#5145CD" style="background-color: #5145CD" class="w-6 h-6 rounded-md"><span class="sr-only">Dark Blue</span></button>
<button type="button" data-hex-color="#B43403" style="background-color: #B43403" class="w-6 h-6 rounded-md"><span class="sr-only">Orange Brown</span></button>
<button type="button" data-hex-color="#FCE96A" style="background-color: #FCE96A" class="w-6 h-6 rounded-md"><span class="sr-only">Light Yellow</span></button>
<button type="button" data-hex-color="#1E429F" style="background-color: #1E429F" class="w-6 h-6 rounded-md"><span class="sr-only">Navy Blue</span></button>
<button type="button" data-hex-color="#768FFD" style="background-color: #768FFD" class="w-6 h-6 rounded-md"><span class="sr-only">Light Purple</span></button>
<button type="button" data-hex-color="#BCF0DA" style="background-color: #BCF0DA" class="w-6 h-6 rounded-md"><span class="sr-only">Light Green</span></button>
<button type="button" data-hex-color="#EBF5FF" style="background-color: #EBF5FF" class="w-6 h-6 rounded-md"><span class="sr-only">Sky Blue</span></button>
<button type="button" data-hex-color="#16BDCA" style="background-color: #16BDCA" class="w-6 h-6 rounded-md"><span class="sr-only">Cyan</span></button>
<button type="button" data-hex-color="#E74694" style="background-color: #E74694" class="w-6 h-6 rounded-md"><span class="sr-only">Pink</span></button>
<button type="button" data-hex-color="#83B0ED" style="background-color: #83B0ED" class="w-6 h-6 rounded-md"><span class="sr-only">Darker Sky Blue</span></button>
<button type="button" data-hex-color="#03543F" style="background-color: #03543F" class="w-6 h-6 rounded-md"><span class="sr-only">Forest Green</span></button>
<button type="button" data-hex-color="#111928" style="background-color: #111928" class="w-6 h-6 rounded-md"><span class="sr-only">Black</span></button>
<button type="button" data-hex-color="#4B5563" style="background-color: #4B5563" class="w-6 h-6 rounded-md"><span class="sr-only">Stone</span></button>
<button type="button" data-hex-color="#6B7280" style="background-color: #6B7280" class="w-6 h-6 rounded-md"><span class="sr-only">Gray</span></button>
<button type="button" data-hex-color="#D1D5DB" style="background-color: #D1D5DB" class="w-6 h-6 rounded-md"><span class="sr-only">Light Gray</span></button>
<button type="button" data-hex-color="#F3F4F6" style="background-color: #F3F4F6" class="w-6 h-6 rounded-md"><span class="sr-only">Cloud Gray</span></button>
<button type="button" data-hex-color="#F3F4F6" style="background-color: #F3F4F6" class="w-6 h-6 rounded-md"><span class="sr-only">Cloud Gray</span></button>
<button type="button" data-hex-color="#F9FAFB" style="background-color: #F9FAFB" class="w-6 h-6 rounded-md"><span class="sr-only">Heaven Gray</span></button>
</div>
<button type="button" id="reset-color" class="text-body bg-neutral-secondary-medium box-border border border-default-medium hover:bg-neutral-tertiary-medium hover:text-heading focus:ring-4 focus:ring-neutral-tertiary shadow-xs font-medium leading-5 rounded-base text-xs px-3 py-1.5 focus:outline-none w-full">Reset color</button>
</div>
<button id="toggleFontFamilyButton" data-dropdown-toggle="fontFamilyDropdown" type="button" data-tooltip-target="tooltip-font-family" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m10.5785 19 4.2979-10.92966c.0369-.09379.1674-.09379.2042 0L19.3785 19m-8.8 0H9.47851m1.09999 0h1.65m7.15 0h-1.65m1.65 0h1.1m-7.7-3.9846h4.4M3 16l1.56685-3.9846m0 0 2.73102-6.94506c.03688-.09379.16738-.09379.20426 0l2.50367 6.94506H4.56685Z"/></svg>
<span class="sr-only">Font family</span>
</button>
<div id="tooltip-font-family" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Font Family
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<div id="fontFamilyDropdown" class="z-10 hidden bg-neutral-primary-medium border border-default-medium rounded-base shadow-lg w-48">
<ul class="p-2 space-y-1 text-sm text-body font-medium" aria-labelledby="toggleFontFamilyButton">
<li>
<button data-font-family="Inter, ui-sans-serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm">Default
</button>
</li>
<li>
<button data-font-family="Arial, sans-serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: Arial, sans-serif;">Arial
</button>
</li>
<li>
<button data-font-family="'Courier New', monospace" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: 'Courier New', monospace;">Courier New
</button>
</li>
<li>
<button data-font-family="Georgia, serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: Georgia, serif;">Georgia
</button>
</li>
<li>
<button data-font-family="'Lucida Sans Unicode', sans-serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: 'Lucida Sans Unicode', sans-serif;">Lucida Sans Unicode
</button>
</li>
<li>
<button data-font-family="Tahoma, sans-serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: Tahoma, sans-serif;">Tahoma
</button>
</li>
<li>
<button data-font-family="'Times New Roman', serif;" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: 'Times New Roman', serif;">Times New Roman
</button>
</li>
<li>
<button data-font-family="'Trebuchet MS', sans-serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: 'Trebuchet MS', sans-serif;">Trebuchet MS
</button>
</li>
<li>
<button data-font-family="Verdana, sans-serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: Verdana, sans-serif;">Verdana
</button>
</li>
</ul>
</div>
<div class="px-1">
<span class="block w-px h-4 bg-neutral-quaternary"></span>
</div>
<button id="toggleLeftAlignButton" type="button" data-tooltip-target="tooltip-left-align" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 6h8m-8 4h12M6 14h8m-8 4h12"/></svg>
<span class="sr-only">Align left</span>
</button>
<div id="tooltip-left-align" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Align left
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleCenterAlignButton" type="button" data-tooltip-target="tooltip-center-align" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 6h8M6 10h12M8 14h8M6 18h12"/></svg>
<span class="sr-only">Align center</span>
</button>
<div id="tooltip-center-align" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Align center
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleRightAlignButton" type="button" data-tooltip-target="tooltip-right-align" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 6h-8m8 4H6m12 4h-8m8 4H6"/></svg>
<span class="sr-only">Align right</span>
</button>
<div id="tooltip-right-align" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Align right
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
<div class="flex items-center gap-2 pt-2 flex-wrap">
<button id="typographyDropdownButton" data-dropdown-toggle="typographyDropdown" class="flex items-center justify-center text-body bg-neutral-primary-strong border border-default-strong hover:bg-neutral-secondary-strongest hover:text-heading focus:ring-4 focus:ring-neutral-tertiary-soft shadow-xs font-medium leading-5 rounded-base text-xs px-3 py-1.5 focus:outline-none" type="button">
Format
<svg class="w-3.5 h-3.5 ms-1.5 -me-0.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 9-7 7-7-7"/></svg>
</button>
<div class="ps-1.5">
<span class="block w-px h-4 bg-neutral-quaternary"></span>
</div>
<!-- Heading Dropdown -->
<div id="typographyDropdown" class="z-10 hidden bg-neutral-primary-medium border border-default-medium rounded-base shadow-lg w-72">
<ul class="p-2 space-y-1 text-sm text-body font-medium" aria-labelledby="typographyDropdownButton">
<li>
<button id="toggleParagraphButton" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Paragraph
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">0</kbd>
</div>
</button>
</li>
<li>
<button data-heading-level="1" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Heading 1
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">1</kbd>
</div>
</button>
</li>
<li>
<button data-heading-level="2" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Heading 2
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">2</kbd>
</div>
</button>
</li>
<li>
<button data-heading-level="3" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Heading 3
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">3</kbd>
</div>
</button>
</li>
<li>
<button data-heading-level="4" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Heading 4
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">4</kbd>
</div>
</button>
</li>
<li>
<button data-heading-level="5" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Heading 5
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">5</kbd>
</div>
</button>
</li>
<li>
<button data-heading-level="6" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Heading 6
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">6</kbd>
</div>
</button>
</li>
</ul>
</div>
<button id="addImageButton" type="button" data-tooltip-target="tooltip-image" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m3 16 5-7 6 6.5m6.5 2.5L16 13l-4.286 6M14 10h.01M4 19h16a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1Z"/></svg>
<span class="sr-only">Add image</span>
</button>
<div id="tooltip-image" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Add image
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="addVideoButton" type="button" data-tooltip-target="tooltip-video" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linejoin="round" stroke-width="2" d="M10 3v4a1 1 0 0 1-1 1H5m14-4v16a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7.914a1 1 0 0 1 .293-.707l3.914-3.914A1 1 0 0 1 9.914 3H18a1 1 0 0 1 1 1ZM9 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1Zm5.697 2.395v-.733l1.269-1.219v2.984l-1.268-1.032Z"/></svg>
<span class="sr-only">Add video</span>
</button>
<div id="tooltip-video" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Add video
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleListButton" type="button" data-tooltip-target="tooltip-list" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M9 8h10M9 12h10M9 16h10M4.99 8H5m-.02 4h.01m0 4H5"/></svg>
<span class="sr-only">Toggle list</span>
</button>
<div id="tooltip-list" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle list
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleOrderedListButton" type="button" data-tooltip-target="tooltip-ordered-list" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6h8m-8 6h8m-8 6h8M4 16a2 2 0 1 1 3.321 1.5L4 20h5M4 5l2-1v6m-2 0h4"/></svg>
<span class="sr-only">Toggle ordered list</span>
</button>
<div id="tooltip-ordered-list" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle ordered list
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleBlockquoteButton" type="button" data-tooltip-target="tooltip-blockquote-list" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11V8a1 1 0 0 0-1-1H6a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1Zm0 0v2a4 4 0 0 1-4 4H5m14-6V8a1 1 0 0 0-1-1h-3a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1Zm0 0v2a4 4 0 0 1-4 4h-1"/></svg>
<span class="sr-only">Toggle blockquote</span>
</button>
<div id="tooltip-blockquote-list" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle blockquote
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleHRButton" type="button" data-tooltip-target="tooltip-hr-list" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M5 12h14"/><path stroke="currentColor" stroke-linecap="round" d="M6 9.5h12m-12-2h12m-12-2h12m-12 13h12m-12-2h12m-12-2h12"/></svg>
<span class="sr-only">Toggle Horizontal Rule</span>
</button>
<div id="tooltip-hr-list" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle Horizontal Rule
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
<div class="px-4 py-2 bg-neutral-primary rounded-b-lg">
<label for="wysiwyg-example" class="sr-only">Publish post</label>
<div id="wysiwyg-example"class="block w-full px-0 text-sm text-body bg-neutral-primary border-0 focus:ring-0"></div>
</div>
</div>
import { Editor } from 'https://esm.sh/@tiptap/core@2.6.6';
import StarterKit from 'https://esm.sh/@tiptap/starter-kit@2.6.6';
import Highlight from 'https://esm.sh/@tiptap/extension-highlight@2.6.6';
import Underline from 'https://esm.sh/@tiptap/extension-underline@2.6.6';
import Link from 'https://esm.sh/@tiptap/extension-link@2.6.6';
import TextAlign from 'https://esm.sh/@tiptap/extension-text-align@2.6.6';
import Image from 'https://esm.sh/@tiptap/extension-image@2.6.6';
import YouTube from 'https://esm.sh/@tiptap/extension-youtube@2.6.6';
import TextStyle from 'https://esm.sh/@tiptap/extension-text-style@2.6.6';
import FontFamily from 'https://esm.sh/@tiptap/extension-font-family@2.6.6';
import { Color } from 'https://esm.sh/@tiptap/extension-color@2.6.6';
import Bold from 'https://esm.sh/@tiptap/extension-bold@2.6.6'; // Import the Bold extension
window.addEventListener('load', function() {
if (document.getElementById("wysiwyg-example")) {
const FontSizeTextStyle = TextStyle.extend({
addAttributes() {
return {
fontSize: {
default: null,
parseHTML: element => element.style.fontSize,
renderHTML: attributes => {
if (!attributes.fontSize) {
return {};
}
return { style: 'font-size: ' + attributes.fontSize };
},
},
};
},
});
const CustomBold = Bold.extend({
// Override the renderHTML method
renderHTML({ mark, HTMLAttributes }) {
const { style, ...rest } = HTMLAttributes;
// Merge existing styles with font-weight
const newStyle = 'font-weight: bold;' + (style ? ' ' + style : '');
return ['span', { ...rest, style: newStyle.trim() }, 0];
},
// Ensure it doesn't exclude other marks
addOptions() {
return {
...this.parent?.(),
HTMLAttributes: {},
};
},
});
// tip tap editor setup
const editor = new Editor({
element: document.querySelector('#wysiwyg-example'),
extensions: [
StarterKit.configure({
textStyle: false,
bold: false,
marks: {
bold: false,
},
}),
// Include the custom Bold extension
CustomBold,
TextStyle,
Color,
FontSizeTextStyle,
FontFamily,
Highlight,
Underline,
Link.configure({
openOnClick: false,
autolink: true,
defaultProtocol: 'https',
}),
TextAlign.configure({
types: ['heading', 'paragraph'],
}),
Image,
YouTube,
],
content: '<p>Flowbite is an <strong>open-source library of UI components</strong> based on the utility-first Tailwind CSS framework featuring dark mode support, a Figma design system, and more.</p><p>It includes all of the commonly used components that a website requires, such as buttons, dropdowns, navigation bars, modals, datepickers, advanced charts and the list goes on.</p><p>Here is an example of a button component:</p><code><button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-base text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Default</button></code><p>Learn more about all components from the <a href="https://flowbite.com/docs/getting-started/introduction/">Flowbite Docs</a>.</p>',
editorProps: {
attributes: {
class: 'format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none',
},
}
});
// set up custom event listeners for the buttons
document.getElementById('toggleBoldButton').addEventListener('click', () => editor.chain().focus().toggleBold().run());
document.getElementById('toggleItalicButton').addEventListener('click', () => editor.chain().focus().toggleItalic().run());
document.getElementById('toggleUnderlineButton').addEventListener('click', () => editor.chain().focus().toggleUnderline().run());
document.getElementById('toggleStrikeButton').addEventListener('click', () => editor.chain().focus().toggleStrike().run());
document.getElementById('toggleHighlightButton').addEventListener('click', () => {
const isHighlighted = editor.isActive('highlight');
// when using toggleHighlight(), judge if is is already highlighted.
editor.chain().focus().toggleHighlight({
color: isHighlighted ? undefined : '#ffc078' // if is already highlighted,unset the highlight color
}).run();
});
document.getElementById('toggleLinkButton').addEventListener('click', () => {
const url = window.prompt('Enter image URL:', 'https://flowbite.com');
editor.chain().focus().toggleLink({ href: url }).run();
});
document.getElementById('removeLinkButton').addEventListener('click', () => {
editor.chain().focus().unsetLink().run()
});
document.getElementById('toggleCodeButton').addEventListener('click', () => {
editor.chain().focus().toggleCode().run();
})
document.getElementById('toggleLeftAlignButton').addEventListener('click', () => {
editor.chain().focus().setTextAlign('left').run();
});
document.getElementById('toggleCenterAlignButton').addEventListener('click', () => {
editor.chain().focus().setTextAlign('center').run();
});
document.getElementById('toggleRightAlignButton').addEventListener('click', () => {
editor.chain().focus().setTextAlign('right').run();
});
document.getElementById('toggleListButton').addEventListener('click', () => {
editor.chain().focus().toggleBulletList().run();
});
document.getElementById('toggleOrderedListButton').addEventListener('click', () => {
editor.chain().focus().toggleOrderedList().run();
});
document.getElementById('toggleBlockquoteButton').addEventListener('click', () => {
editor.chain().focus().toggleBlockquote().run();
});
document.getElementById('toggleHRButton').addEventListener('click', () => {
editor.chain().focus().setHorizontalRule().run();
});
document.getElementById('addImageButton').addEventListener('click', () => {
const url = window.prompt('Enter image URL:', 'https://placehold.co/600x400');
if (url) {
editor.chain().focus().setImage({ src: url }).run();
}
});
document.getElementById('addVideoButton').addEventListener('click', () => {
const url = window.prompt('Enter YouTube URL:', 'https://www.youtube.com/watch?v=KaLxCiilHns');
if (url) {
editor.commands.setYoutubeVideo({
src: url,
width: 640,
height: 480,
})
}
});
// typography dropdown
const typographyDropdown = FlowbiteInstances.getInstance('Dropdown', 'typographyDropdown');
document.getElementById('toggleParagraphButton').addEventListener('click', () => {
editor.chain().focus().setParagraph().run();
typographyDropdown.hide();
});
document.querySelectorAll('[data-heading-level]').forEach((button) => {
button.addEventListener('click', () => {
const level = button.getAttribute('data-heading-level');
editor.chain().focus().toggleHeading({ level: parseInt(level) }).run()
typographyDropdown.hide();
});
});
const textSizeDropdown = FlowbiteInstances.getInstance('Dropdown', 'textSizeDropdown');
// Loop through all elements with the data-text-size attribute
document.querySelectorAll('[data-text-size]').forEach((button) => {
button.addEventListener('click', () => {
const fontSize = button.getAttribute('data-text-size');
// Apply the selected font size via pixels using the TipTap editor chain
editor.chain().focus().setMark('textStyle', { fontSize }).run();
// Hide the dropdown after selection
textSizeDropdown.hide();
});
});
// Listen for color picker changes
const colorPicker = document.getElementById('color');
colorPicker.addEventListener('input', (event) => {
const selectedColor = event.target.value;
// Apply the selected color to the selected text
editor.chain().focus().setColor(selectedColor).run();
})
document.querySelectorAll('[data-hex-color]').forEach((button) => {
button.addEventListener('click', () => {
const selectedColor = button.getAttribute('data-hex-color');
// Apply the selected color to the selected text
editor.chain().focus().setColor(selectedColor).run();
});
});
document.getElementById('reset-color').addEventListener('click', () => {
editor.commands.unsetColor();
})
const fontFamilyDropdown = FlowbiteInstances.getInstance('Dropdown', 'fontFamilyDropdown');
// Loop through all elements with the data-font-family attribute
document.querySelectorAll('[data-font-family]').forEach((button) => {
button.addEventListener('click', () => {
const fontFamily = button.getAttribute('data-font-family');
// Apply the selected font size via pixels using the TipTap editor chain
editor.chain().focus().setFontFamily(fontFamily).run();
// Hide the dropdown after selection
fontFamilyDropdown.hide();
});
});
}
})
Notice: there is a known issue from TipTap when splitting blocks (ie. using enter to create break lines) and using the bullet list item. A quickfix for v2.6.6 when using CDN is to match the import statements:
- wysiwyg.html
<script type="importmap">
{
"imports": {
"https://esm.sh/v135/prosemirror-model@1.22.3/es2022/prosemirror-model.mjs": "https://esm.sh/v135/prosemirror-model@1.19.3/es2022/prosemirror-model.mjs",
"https://esm.sh/v135/prosemirror-model@1.22.1/es2022/prosemirror-model.mjs": "https://esm.sh/v135/prosemirror-model@1.19.3/es2022/prosemirror-model.mjs"
}
}
</script>
If you’re importing the package with Yarn or NPM then you need to add this in your package.json file:
- wysiwyg.js
// when using Yarn
"resolutions": {
"prosemirror-model": "1.19.3"
}
// when using NPM
"overrides": {
"prosemirror-model": "1.19.3"
}
We recommend later checking the Tip Tap library for a proper update to prevent this quickfix in the future.
Text formatting #
Use this example of a WYSIWYG text editor to enable typography styling, formatting and marking such as underline, bold, italic, strikethrough, code, highlight and also selecting text size, color, font family and more using the utility classes from Tailwind CSS.
<div class="w-full bg-neutral-secondary-medium border border-default-medium rounded-base">
<div class="p-2 border-b border-default-medium">
<div class="flex flex-wrap items-center">
<div class="flex items-center space-x-1 rtl:space-x-reverse flex-wrap">
<button id="toggleBoldButton" data-tooltip-target="tooltip-bold" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5h4.5a3.5 3.5 0 1 1 0 7H8m0-7v7m0-7H6m2 7h6.5a3.5 3.5 0 1 1 0 7H8m0-7v7m0 0H6"/></svg>
<span class="sr-only">Bold</span>
</button>
<div id="tooltip-bold" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle bold
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleItalicButton" data-tooltip-target="tooltip-italic" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m8.874 19 6.143-14M6 19h6.33m-.66-14H18"/></svg>
<span class="sr-only">Italic</span>
</button>
<div id="tooltip-italic" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle italic
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleUnderlineButton" data-tooltip-target="tooltip-underline" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M6 19h12M8 5v9a4 4 0 0 0 8 0V5M6 5h4m4 0h4"/></svg>
<span class="sr-only">Underline</span>
</button>
<div id="tooltip-underline" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle underline
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleStrikeButton" data-tooltip-target="tooltip-strike" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 6.2V5h12v1.2M7 19h6m.2-14-1.677 6.523M9.6 19l1.029-4M5 5l6.523 6.523M19 19l-7.477-7.477"/></svg>
<span class="sr-only">Strike</span>
</button>
<div id="tooltip-strike" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle strike
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleSubscriptButton" data-tooltip-target="tooltip-subscript" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 20h-4v-.5c1.0989-1.0329 3.75-2.5 3.75-3.5v-1.0001c0-.5523-.4477-.9999-1-.9999H17c-.5522 0-1 .4477-1 1M4.00004 4l9.12206 11.3932m0-11.3932L4 15.3932"/></svg>
<span class="sr-only">Subscript</span>
</button>
<div id="tooltip-subscript" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle subscript
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleSuperscriptButton" data-tooltip-target="tooltip-superscript" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 10h-4v-.5C17.0989 8.46711 19.75 7 19.75 6V4.99989c0-.55228-.4477-.99989-1-.99989H17c-.5523 0-1 .44772-1 1M4 7.30341l9.1221 11.39319m0-11.39319L4 18.6966"/></svg>
<span class="sr-only">Superscript</span>
</button>
<div id="tooltip-superscript" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle superscript
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleHighlightButton" data-tooltip-target="tooltip-highlight" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M9 20H5.5c-.27614 0-.5-.2239-.5-.5v-3c0-.2761.22386-.5.5-.5h13c.2761 0 .5.2239.5.5v3c0 .2761-.2239.5-.5.5H18m-6-1 1.42 1.8933c.04.0534.12.0534.16 0L15 19m-7-6 3.9072-9.76789c.0335-.08381.1521-.08381.1856 0L16 13m-8 0H7m1 0h1.5m6.5 0h-1.5m1.5 0h1m-7-3.00001h4"/></svg>
<span class="sr-only">Highlight</span>
</button>
<div id="tooltip-highlight" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle highlight
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleCodeButton" type="button" data-tooltip-target="tooltip-code" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m8 8-4 4 4 4m8 0 4-4-4-4m-2-3-4 14"/></svg>
<span class="sr-only">Code</span>
</button>
<div id="tooltip-code" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Format code
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleTextSizeButton" data-dropdown-toggle="textSizeDropdown" type="button" data-tooltip-target="tooltip-text-size" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 6.2V5h11v1.2M8 5v14m-3 0h6m2-6.8V11h8v1.2M17 11v8m-1.5 0h3"/></svg>
<span class="sr-only">Text size</span>
</button>
<div id="tooltip-text-size" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Text size
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<div id="textSizeDropdown" class="z-10 hidden bg-neutral-primary-medium border border-default-medium rounded-base shadow-lg w-72">
<ul class="p-2 space-y-1 text-sm text-body font-medium" aria-labelledby="toggleTextSizeButton">
<li>
<button data-text-size="16px" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm">16px (Default)
</button>
</li>
<li>
<button data-text-size="12px" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-xs">12px (Tiny)
</button>
</li>
<li>
<button data-text-size="14px" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm">14px (Small)
</button>
</li>
<li>
<button data-text-size="18px" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-lg">18px (Lead)
</button>
</li>
<li>
<button data-text-size="24px" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-2xl">24px (Large)
</button>
</li>
<li>
<button data-text-size="36px" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-4xl">36px (Huge)
</button>
</li>
</ul>
</div>
<button id="toggleTextColorButton" data-dropdown-toggle="textColorDropdown" type="button" data-tooltip-target="tooltip-text-color" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="m6.08169 15.9817 1.57292-4m-1.57292 4h-1.1m1.1 0h1.65m-.07708-4 2.72499-6.92967c.0368-.09379.1673-.09379.2042 0l2.725 6.92967m-5.65419 0h-.00607m.00607 0h5.65419m0 0 .6169 1.569m5.1104 4.453c0 1.1025-.8543 1.9963-1.908 1.9963s-1.908-.8938-1.908-1.9963c0-1.1026 1.908-4.1275 1.908-4.1275s1.908 3.0249 1.908 4.1275Z"/></svg>
<span class="sr-only">Text color</span>
</button>
<div id="tooltip-text-color" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Text color
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<div id="textColorDropdown" class="z-10 hidden w-48 rounded-sm bg-neutral-primary-soft p-2 shadow-sm">
<div class="grid grid-cols-6 gap-2 group mb-3 items-center p-1.5 rounded hover:bg-neutral-tertiary">
<input type="color" id="color" value="#e66465" class="border-default-medium
border bg-neutral-tertiary rounded p-px px-1 w-full h-8 col-span-3" />
<label for="color" class="text-body text-xs font-medium col-span-3 group-hover:text-heading">Pick a color</label>
</div>
<div class="grid grid-cols-6 gap-1 mb-3">
<button type="button" data-hex-color="#1A56DB" style="background-color: #1A56DB" class="w-6 h-6 rounded-md"><span class="sr-only">Blue</span></button>
<button type="button" data-hex-color="#0E9F6E" style="background-color: #0E9F6E" class="w-6 h-6 rounded-md"><span class="sr-only">Green</span></button>
<button type="button" data-hex-color="#FACA15" style="background-color: #FACA15" class="w-6 h-6 rounded-md"><span class="sr-only">Yellow</span></button>
<button type="button" data-hex-color="#F05252" style="background-color: #F05252" class="w-6 h-6 rounded-md"><span class="sr-only">Red</span></button>
<button type="button" data-hex-color="#FF8A4C" style="background-color: #FF8A4C" class="w-6 h-6 rounded-md"><span class="sr-only">Orange</span></button>
<button type="button" data-hex-color="#0694A2" style="background-color: #0694A2" class="w-6 h-6 rounded-md"><span class="sr-only">Teal</span></button>
<button type="button" data-hex-color="#B4C6FC" style="background-color: #B4C6FC" class="w-6 h-6 rounded-md"><span class="sr-only">Light indigo</span></button>
<button type="button" data-hex-color="#8DA2FB" style="background-color: #8DA2FB" class="w-6 h-6 rounded-md"><span class="sr-only">Indigo</span></button>
<button type="button" data-hex-color="#5145CD" style="background-color: #5145CD" class="w-6 h-6 rounded-md"><span class="sr-only">Purple</span></button>
<button type="button" data-hex-color="#771D1D" style="background-color: #771D1D" class="w-6 h-6 rounded-md"><span class="sr-only">Brown</span></button>
<button type="button" data-hex-color="#FCD9BD" style="background-color: #FCD9BD" class="w-6 h-6 rounded-md"><span class="sr-only">Light orange</span></button>
<button type="button" data-hex-color="#99154B" style="background-color: #99154B" class="w-6 h-6 rounded-md"><span class="sr-only">Bordo</span></button>
<button type="button" data-hex-color="#7E3AF2" style="background-color: #7E3AF2" class="w-6 h-6 rounded-md"><span class="sr-only">Dark Purple</span></button>
<button type="button" data-hex-color="#CABFFD" style="background-color: #CABFFD" class="w-6 h-6 rounded-md"><span class="sr-only">Light</span></button>
<button type="button" data-hex-color="#D61F69" style="background-color: #D61F69" class="w-6 h-6 rounded-md"><span class="sr-only">Dark Pink</span></button>
<button type="button" data-hex-color="#F8B4D9" style="background-color: #F8B4D9" class="w-6 h-6 rounded-md"><span class="sr-only">Pink</span></button>
<button type="button" data-hex-color="#F6C196" style="background-color: #F6C196" class="w-6 h-6 rounded-md"><span class="sr-only">Cream</span></button>
<button type="button" data-hex-color="#A4CAFE" style="background-color: #A4CAFE" class="w-6 h-6 rounded-md"><span class="sr-only">Light Blue</span></button>
<button type="button" data-hex-color="#5145CD" style="background-color: #5145CD" class="w-6 h-6 rounded-md"><span class="sr-only">Dark Blue</span></button>
<button type="button" data-hex-color="#B43403" style="background-color: #B43403" class="w-6 h-6 rounded-md"><span class="sr-only">Orange Brown</span></button>
<button type="button" data-hex-color="#FCE96A" style="background-color: #FCE96A" class="w-6 h-6 rounded-md"><span class="sr-only">Light Yellow</span></button>
<button type="button" data-hex-color="#1E429F" style="background-color: #1E429F" class="w-6 h-6 rounded-md"><span class="sr-only">Navy Blue</span></button>
<button type="button" data-hex-color="#768FFD" style="background-color: #768FFD" class="w-6 h-6 rounded-md"><span class="sr-only">Light Purple</span></button>
<button type="button" data-hex-color="#BCF0DA" style="background-color: #BCF0DA" class="w-6 h-6 rounded-md"><span class="sr-only">Light Green</span></button>
<button type="button" data-hex-color="#EBF5FF" style="background-color: #EBF5FF" class="w-6 h-6 rounded-md"><span class="sr-only">Sky Blue</span></button>
<button type="button" data-hex-color="#16BDCA" style="background-color: #16BDCA" class="w-6 h-6 rounded-md"><span class="sr-only">Cyan</span></button>
<button type="button" data-hex-color="#E74694" style="background-color: #E74694" class="w-6 h-6 rounded-md"><span class="sr-only">Pink</span></button>
<button type="button" data-hex-color="#83B0ED" style="background-color: #83B0ED" class="w-6 h-6 rounded-md"><span class="sr-only">Darker Sky Blue</span></button>
<button type="button" data-hex-color="#03543F" style="background-color: #03543F" class="w-6 h-6 rounded-md"><span class="sr-only">Forest Green</span></button>
<button type="button" data-hex-color="#111928" style="background-color: #111928" class="w-6 h-6 rounded-md"><span class="sr-only">Black</span></button>
<button type="button" data-hex-color="#4B5563" style="background-color: #4B5563" class="w-6 h-6 rounded-md"><span class="sr-only">Stone</span></button>
<button type="button" data-hex-color="#6B7280" style="background-color: #6B7280" class="w-6 h-6 rounded-md"><span class="sr-only">Gray</span></button>
<button type="button" data-hex-color="#D1D5DB" style="background-color: #D1D5DB" class="w-6 h-6 rounded-md"><span class="sr-only">Light Gray</span></button>
<button type="button" data-hex-color="#F3F4F6" style="background-color: #F3F4F6" class="w-6 h-6 rounded-md"><span class="sr-only">Cloud Gray</span></button>
<button type="button" data-hex-color="#F3F4F6" style="background-color: #F3F4F6" class="w-6 h-6 rounded-md"><span class="sr-only">Cloud Gray</span></button>
<button type="button" data-hex-color="#F9FAFB" style="background-color: #F9FAFB" class="w-6 h-6 rounded-md"><span class="sr-only">Heaven Gray</span></button>
</div>
<button type="button" id="reset-color" class="text-body bg-neutral-secondary-medium box-border border border-default-medium hover:bg-neutral-tertiary-medium hover:text-heading focus:ring-4 focus:ring-neutral-tertiary shadow-xs font-medium leading-5 rounded-base text-xs px-3 py-1.5 focus:outline-none w-full">Reset color</button>
</div>
<button id="toggleFontFamilyButton" data-dropdown-toggle="fontFamilyDropdown" type="button" data-tooltip-target="tooltip-font-family" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m10.6 19 4.298-10.93a.11.11 0 0 1 .204 0L19.4 19m-8.8 0H9.5m1.1 0h1.65m7.15 0h-1.65m1.65 0h1.1m-7.7-3.985h4.4M3.021 16l1.567-3.985m0 0L7.32 5.07a.11.11 0 0 1 .205 0l2.503 6.945h-5.44Z"/></svg>
<span class="sr-only">Font family</span>
</button>
<div id="tooltip-font-family" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Font Family
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<div id="fontFamilyDropdown" class="z-10 hidden bg-neutral-primary-medium border border-default-medium rounded-base shadow-lg w-48">
<ul class="p-2 space-y-1 text-sm text-body font-medium" aria-labelledby="toggleFontFamilyButton">
<li>
<button data-font-family="Inter, ui-sans-serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm">Default
</button>
</li>
<li>
<button data-font-family="Arial, sans-serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: Arial, sans-serif;">Arial
</button>
</li>
<li>
<button data-font-family="'Courier New', monospace" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: 'Courier New', monospace;">Courier New
</button>
</li>
<li>
<button data-font-family="Georgia, serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: Georgia, serif;">Georgia
</button>
</li>
<li>
<button data-font-family="'Lucida Sans Unicode', sans-serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: 'Lucida Sans Unicode', sans-serif;">Lucida Sans Unicode
</button>
</li>
<li>
<button data-font-family="Tahoma, sans-serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: Tahoma, sans-serif;">Tahoma
</button>
</li>
<li>
<button data-font-family="'Times New Roman', serif;" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: 'Times New Roman', serif;">Times New Roman
</button>
</li>
<li>
<button data-font-family="'Trebuchet MS', sans-serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: 'Trebuchet MS', sans-serif;">Trebuchet MS
</button>
</li>
<li>
<button data-font-family="Verdana, sans-serif" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded text-sm" style="font-family: Verdana, sans-serif;">Verdana
</button>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="p-3 rounded-b-base bg-neutral-primary">
<label for="wysiwyg-text-example" class="sr-only">Write comment</label>
<div id="wysiwyg-text-example" class="block w-full px-0 text-sm text-body bg-neutral-primary border-0 focus:ring-0"></div>
</div>
</div>
import { Editor } from 'https://esm.sh/@tiptap/core@2.6.6';
import StarterKit from 'https://esm.sh/@tiptap/starter-kit@2.6.6';
import Highlight from 'https://esm.sh/@tiptap/extension-highlight@2.6.6';
import Underline from 'https://esm.sh/@tiptap/extension-underline@2.6.6';
import Subscript from 'https://esm.sh/@tiptap/extension-subscript@2.6.6';
import Superscript from 'https://esm.sh/@tiptap/extension-superscript@2.6.6';
import TextStyle from 'https://esm.sh/@tiptap/extension-text-style@2.6.6';
import FontFamily from 'https://esm.sh/@tiptap/extension-font-family@2.6.6';
import { Color } from 'https://esm.sh/@tiptap/extension-color@2.6.6';
import Bold from 'https://esm.sh/@tiptap/extension-bold@2.6.6';
window.addEventListener('load', function() {
if (document.getElementById("wysiwyg-text-example")) {
const FontSizeTextStyle = TextStyle.extend({
addAttributes() {
return {
fontSize: {
default: null,
parseHTML: element => element.style.fontSize,
renderHTML: attributes => {
if (!attributes.fontSize) {
return {};
}
return { style: 'font-size: ' + attributes.fontSize };
},
},
};
},
});
const CustomBold = Bold.extend({
// Override the renderHTML method
renderHTML({ HTMLAttributes }) {
return ['span', { ...HTMLAttributes, style: 'font-weight: bold;' }, 0];
},
// Ensure it doesn't exclude other marks
excludes: '',
});
// tip tap editor setup
const editor = new Editor({
element: document.querySelector('#wysiwyg-text-example'),
extensions: [
StarterKit.configure({
textStyle: false,
bold: false,
marks: {
bold: false,
},
}),
// Include the custom Bold extension
CustomBold,
Highlight,
Underline,
Subscript,
Superscript,
TextStyle,
FontSizeTextStyle,
Color,
FontFamily
],
content: '<p>Flowbite React is an <strong>open-source library of UI components</strong> built using React and Tailwind CSS. It supports dark mode, a Figma design system, and more.</p><p>It includes essential components for web apps like buttons, dropdowns, navigation bars, modals, datepickers, and charts, all optimized for React.</p><p>Example button component in Flowbite React:</p><code>import { Button } from 'flowbite-react'; <Button color="blue">Default</Button></code><p>These components can also be easily customized using the theme props from the Flowbite Docs and allows you to add your own Tailwind CSS utility classes to override the default styles.</p><p>Explore more components and props values in the Flowbite Docs.</p>',
editorProps: {
attributes: {
class: 'format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none',
},
}
});
// set up custom event listeners for the buttons
document.getElementById('toggleBoldButton').addEventListener('click', () => editor.chain().focus().toggleBold().run());
document.getElementById('toggleItalicButton').addEventListener('click', () => editor.chain().focus().toggleItalic().run());
document.getElementById('toggleUnderlineButton').addEventListener('click', () => editor.chain().focus().toggleUnderline().run());
document.getElementById('toggleStrikeButton').addEventListener('click', () => editor.chain().focus().toggleStrike().run());
document.getElementById('toggleSubscriptButton').addEventListener('click', () => editor.chain().focus().toggleSubscript().run());
document.getElementById('toggleSuperscriptButton').addEventListener('click', () => editor.chain().focus().toggleSuperscript().run());
document.getElementById('toggleHighlightButton').addEventListener('click', () => {
const isHighlighted = editor.isActive('highlight');
// when using toggleHighlight(), judge if is is already highlighted.
editor.chain().focus().toggleHighlight({
color: isHighlighted ? undefined : '#ffc078' // if is already highlighted,unset the highlight color
}).run();
});
document.getElementById('toggleCodeButton').addEventListener('click', () => {
editor.chain().focus().toggleCode().run();
});
const textSizeDropdown = FlowbiteInstances.getInstance('Dropdown', 'textSizeDropdown');
// Loop through all elements with the data-text-size attribute
document.querySelectorAll('[data-text-size]').forEach((button) => {
button.addEventListener('click', () => {
const fontSize = button.getAttribute('data-text-size');
// Apply the selected font size via pixels using the TipTap editor chain
editor.chain().focus().setMark('textStyle', { fontSize }).run();
// Hide the dropdown after selection
textSizeDropdown.hide();
});
});
// Listen for color picker changes
const colorPicker = document.getElementById('color');
colorPicker.addEventListener('input', (event) => {
const selectedColor = event.target.value;
// Apply the selected color to the selected text
editor.chain().focus().setColor(selectedColor).run();
})
document.querySelectorAll('[data-hex-color]').forEach((button) => {
button.addEventListener('click', () => {
const selectedColor = button.getAttribute('data-hex-color');
// Apply the selected color to the selected text
editor.chain().focus().setColor(selectedColor).run();
});
});
document.getElementById('reset-color').addEventListener('click', () => {
editor.commands.unsetColor();
})
const fontFamilyDropdown = FlowbiteInstances.getInstance('Dropdown', 'fontFamilyDropdown');
// Loop through all elements with the data-font-family attribute
document.querySelectorAll('[data-font-family]').forEach((button) => {
button.addEventListener('click', () => {
const fontFamily = button.getAttribute('data-font-family');
// Apply the selected font size via pixels using the TipTap editor chain
editor.chain().focus().setFontFamily(fontFamily).run();
// Hide the dropdown after selection
fontFamilyDropdown.hide();
});
});
}
})
Text alignment #
Enable text alignment to the left, center, right, and justify for the content inside of the WYSIWYG component.
<div class="w-full bg-neutral-secondary-medium border border-default-medium rounded-base">
<div class="p-2 border-b border-default-medium">
<div class="flex flex-wrap items-center">
<div class="flex items-center space-x-1 rtl:space-x-reverse flex-wrap">
<button id="toggleLeftAlignButton" type="button" data-tooltip-target="tooltip-left-align" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 6h8m-8 4h12M6 14h8m-8 4h12"/></svg>
<span class="sr-only">Align left</span>
</button>
<div id="tooltip-left-align" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Align left
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleCenterAlignButton" type="button" data-tooltip-target="tooltip-center-align" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 6h8M6 10h12M8 14h8M6 18h12"/></svg>
<span class="sr-only">Align center</span>
</button>
<div id="tooltip-center-align" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Align center
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleRightAlignButton" type="button" data-tooltip-target="tooltip-right-align" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 6h-8m8 4H6m12 4h-8m8 4H6"/>
</svg>
<span class="sr-only">Align right</span>
</button>
<div id="tooltip-right-align" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Align right
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleJustifyButton" type="button" data-tooltip-target="tooltip-justify" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 6H6m12 4H6m12 4H6m12 4H6"/>
</svg>
<span class="sr-only">Justify</span>
</button>
<div id="tooltip-justify" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Justify text
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
</div>
<div class="p-3 rounded-b-base bg-neutral-primary">
<label for="wysiwyg-alignment-example" class="sr-only">Publish post</label>
<div id="wysiwyg-alignment-example"class="block w-full px-0 text-sm text-body bg-neutral-primary border-0 focus:ring-0"></div>
</div>
</div>
import { Editor } from 'https://esm.sh/@tiptap/core@2.6.6';
import StarterKit from 'https://esm.sh/@tiptap/starter-kit@2.6.6';
import Highlight from 'https://esm.sh/@tiptap/extension-highlight@2.6.6';
import Underline from 'https://esm.sh/@tiptap/extension-underline@2.6.6';
import Link from 'https://esm.sh/@tiptap/extension-link@2.6.6';
import TextAlign from 'https://esm.sh/@tiptap/extension-text-align@2.6.6';
import Image from 'https://esm.sh/@tiptap/extension-image@2.6.6';
import YouTube from 'https://esm.sh/@tiptap/extension-youtube@2.6.6';
window.addEventListener('load', function() {
if (document.getElementById("wysiwyg-alignment-example")) {
// tip tap editor setup
const editor = new Editor({
element: document.querySelector('#wysiwyg-alignment-example'),
extensions: [
StarterKit,
Highlight,
Underline,
Link.configure({
openOnClick: false,
autolink: true,
defaultProtocol: 'https',
}),
TextAlign.configure({
types: ['heading', 'paragraph'],
}),
Image,
YouTube
],
content: '<p>Flowbite is an <strong>open-source library of UI components</strong> based on the utility-first Tailwind CSS framework featuring dark mode support, a Figma design system, and more.</p><p>It includes all of the commonly used components that a website requires, such as buttons, dropdowns, navigation bars, modals, datepickers, advanced charts and the list goes on.</p><p>Here is an example of a button component:</p><code><button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-base text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Default</button></code><p>Learn more about all components from the <a href="https://flowbite.com/docs/getting-started/introduction/">Flowbite Docs</a>.</p>',
editorProps: {
attributes: {
class: 'format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none',
},
}
});
// set up custom event listeners for the buttons
document.getElementById('toggleLeftAlignButton').addEventListener('click', () => {
editor.chain().focus().setTextAlign('left').run();
});
document.getElementById('toggleCenterAlignButton').addEventListener('click', () => {
editor.chain().focus().setTextAlign('center').run();
});
document.getElementById('toggleRightAlignButton').addEventListener('click', () => {
editor.chain().focus().setTextAlign('right').run();
});
document.getElementById('toggleJustifyButton').addEventListener('click', () => {
editor.chain().focus().setTextAlign('justify').run();
});
}
})
Typography elements #
Use this example to create typography elements like bullet lists, ordered lists, blockquotes, horizontal rules, paragraphs, headings, code blocks based on Tailwind CSS utility classees and the Flowbite API.
<div class="w-full bg-neutral-secondary-medium border border-default-medium rounded-base">
<div class="p-2 border-b border-default-medium">
<div class="flex items-center gap-2">
<button id="typographyDropdownButton" data-dropdown-toggle="typographyDropdown" class="flex items-center justify-center text-body bg-neutral-primary-strong border border-default-strong hover:bg-neutral-secondary-strongest hover:text-heading focus:ring-4 focus:ring-neutral-tertiary-soft shadow-xs font-medium leading-5 rounded-base text-xs px-3 py-1.5 focus:outline-none" type="button">
Format
<svg class="w-3.5 h-3.5 ms-1.5 -me-0.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 9-7 7-7-7"/></svg>
</button>
<div class="ps-1.5">
<span class="block w-px h-4 bg-neutral-tertiary"></span>
</div>
<!-- Heading Dropdown -->
<div id="typographyDropdown" class="z-10 hidden bg-neutral-primary-medium border border-default-medium rounded-base shadow-lg w-72">
<ul class="p-2 space-y-1 text-sm text-body font-medium" aria-labelledby="typographyDropdownButton">
<li>
<button id="toggleParagraphButton" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Paragraph
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">0</kbd>
</div>
</button>
</li>
<li>
<button data-heading-level="1" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Heading 1
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">1</kbd>
</div>
</button>
</li>
<li>
<button data-heading-level="2" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Heading 2
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">2</kbd>
</div>
</button>
</li>
<li>
<button data-heading-level="3" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Heading 3
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">3</kbd>
</div>
</button>
</li>
<li>
<button data-heading-level="4" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Heading 4
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">4</kbd>
</div>
</button>
</li>
<li>
<button data-heading-level="5" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Heading 5
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">5</kbd>
</div>
</button>
</li>
<li>
<button data-heading-level="6" type="button" class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded">Heading 6
<div class="space-x-1.5 ms-4">
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Cmd</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">Alt</kbd>
<kbd class="px-2 py-1 text-xs font-semibold text-body bg-neutral-tertiary-medium border border-default-strong rounded">6</kbd>
</div>
</button>
</li>
</ul>
</div>
<div class="flex items-center space-x-1 rtl:space-x-reverse flex-wrap">
<button id="toggleCodeBlockButton" type="button" data-tooltip-target="tooltip-code-block" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m8 8-4 4 4 4m8 0 4-4-4-4m-2-3-4 14"/>
</svg>
<span class="sr-only">Toggle code block</span>
</button>
<div id="tooltip-code-block" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle code block
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleListButton" type="button" data-tooltip-target="tooltip-list" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M9 8h10M9 12h10M9 16h10M4.99 8H5m-.02 4h.01m0 4H5"/>
</svg>
<span class="sr-only">Toggle list</span>
</button>
<div id="tooltip-list" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle list
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleOrderedListButton" type="button" data-tooltip-target="tooltip-ordered-list" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6h8m-8 6h8m-8 6h8M4 16a2 2 0 1 1 3.321 1.5L4 20h5M4 5l2-1v6m-2 0h4"/>
</svg>
<span class="sr-only">Create ordered list</span>
</button>
<div id="tooltip-ordered-list" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle ordered list
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleBlockquoteButton" type="button" data-tooltip-target="tooltip-blockquote-list" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M6 6a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h3a3 3 0 0 1-3 3H5a1 1 0 1 0 0 2h1a5 5 0 0 0 5-5V8a2 2 0 0 0-2-2H6Zm9 0a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h3a3 3 0 0 1-3 3h-1a1 1 0 1 0 0 2h1a5 5 0 0 0 5-5V8a2 2 0 0 0-2-2h-3Z" clip-rule="evenodd"/>
</svg>
<span class="sr-only">Toggle blockquote</span>
</button>
<div id="tooltip-blockquote-list" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle blockquote
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleHRButton" type="button" data-tooltip-target="tooltip-hr-list" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M5 12h14"/>
<path stroke="currentColor" stroke-linecap="round" d="M6 9.5h12m-12 9h12M6 7.5h12m-12 9h12M6 5.5h12m-12 9h12"/>
</svg>
<span class="sr-only">Toggle Horizontal Rule</span>
</button>
<div id="tooltip-hr-list" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle Horizontal Rule
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
</div>
<div class="p-3 rounded-b-base bg-neutral-primary">
<label for="wysiwyg-typography-example" class="sr-only">Publish post</label>
<div id="wysiwyg-typography-example"class="block w-full px-0 text-sm text-body bg-neutral-primary border-0 focus:ring-0"></div>
</div>
</div>
import { Editor } from 'https://esm.sh/@tiptap/core@2.6.6';
import StarterKit from 'https://esm.sh/@tiptap/starter-kit@2.6.6';
window.addEventListener('load', function() {
if (document.getElementById("wysiwyg-typography-example")) {
// tip tap editor setup
const editor = new Editor({
element: document.querySelector('#wysiwyg-typography-example'),
extensions: [
StarterKit
],
content: '<p>Flowbite is an <strong>open-source library of UI components</strong> based on the utility-first Tailwind CSS framework featuring dark mode support, a Figma design system, and more.</p><p>It includes all of the commonly used components that a website requires, such as buttons, dropdowns, navigation bars, modals, datepickers, advanced charts and the list goes on.</p><ul><li>Over 600+ open-source UI components</li><li>Supports dark mode and RTL</li><li>Available in React, Vue, Svelte frameworks</li></ul><p>Here is an example of a button component:</p><pre><code><button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-base text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Default</button></code></pre><p>Learn more about all components from the <a href="https://flowbite.com/docs/getting-started/introduction/">Flowbite Docs</a>.</p>',
editorProps: {
attributes: {
class: 'format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none',
},
}
});
// set up custom event listeners for the buttons
document.getElementById('toggleListButton').addEventListener('click', () => {
editor.chain().focus().toggleBulletList().run();
});
document.getElementById('toggleOrderedListButton').addEventListener('click', () => {
editor.chain().focus().toggleOrderedList().run();
});
document.getElementById('toggleBlockquoteButton').addEventListener('click', () => {
editor.chain().focus().toggleBlockquote().run();
});
document.getElementById('toggleHRButton').addEventListener('click', () => {
editor.chain().focus().setHorizontalRule().run();
});
document.getElementById('toggleCodeBlockButton').addEventListener('click', () => {
editor.chain().focus().toggleCodeBlock().run();
});
// typography dropdown
const typographyDropdown = FlowbiteInstances.getInstance('Dropdown', 'typographyDropdown');
document.getElementById('toggleParagraphButton').addEventListener('click', () => {
editor.chain().focus().setParagraph().run();
typographyDropdown.hide();
});
document.querySelectorAll('[data-heading-level]').forEach((button) => {
button.addEventListener('click', () => {
const level = button.getAttribute('data-heading-level');
editor.chain().focus().toggleHeading({ level: parseInt(level) }).run()
typographyDropdown.hide();
});
});
}
})
Configuring links #
Use this example to add and remove anchor links for the content inside of the WYSIWYG text editor.
<div class="w-full bg-neutral-secondary-medium border border-default-medium rounded-base">
<div class="p-2 border-b border-default-medium">
<div class="flex flex-wrap items-center">
<div class="flex items-center space-x-1 rtl:space-x-reverse flex-wrap">
<button id="toggleLinkButton" data-tooltip-target="tooltip-link" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.213 9.787a3.391 3.391 0 0 0-4.795 0l-3.425 3.426a3.39 3.39 0 0 0 4.795 4.794l.321-.304m-.321-4.49a3.39 3.39 0 0 0 4.795 0l3.424-3.426a3.39 3.39 0 0 0-4.794-4.795l-1.028.961"/>
</svg>
<span class="sr-only">Link</span>
</button>
<div id="tooltip-link" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Add link
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="removeLinkButton" data-tooltip-target="tooltip-remove-link" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M13.2 9.8a3.4 3.4 0 0 0-4.8 0L5 13.2A3.4 3.4 0 0 0 9.8 18l.3-.3m-.3-4.5a3.4 3.4 0 0 0 4.8 0L18 9.8A3.4 3.4 0 0 0 13.2 5l-1 1m7.4 14-1.8-1.8m0 0L16 16.4m1.8 1.8 1.8-1.8m-1.8 1.8L16 20"/>
</svg>
<span class="sr-only">Remove link</span>
</button>
<div id="tooltip-remove-link" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Remove link
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
</div>
<div class="p-3 rounded-b-base bg-neutral-primary">
<label for="wysiwyg-links-example" class="sr-only">Publish post</label>
<div id="wysiwyg-links-example"class="block w-full px-0 text-sm text-body bg-neutral-primary border-0 focus:ring-0"></div>
</div>
</div>
import { Editor } from 'https://esm.sh/@tiptap/core@2.6.6';
import StarterKit from 'https://esm.sh/@tiptap/starter-kit@2.6.6';
import Link from 'https://esm.sh/@tiptap/extension-link@2.6.6';
window.addEventListener('load', function() {
if (document.getElementById("wysiwyg-links-example")) {
// tip tap editor setup
const editor = new Editor({
element: document.querySelector('#wysiwyg-links-example'),
extensions: [
StarterKit,
Link.configure({
openOnClick: false,
autolink: true,
defaultProtocol: 'https',
})
],
content: '<p>Flowbite is an <strong>open-source library of UI components</strong> based on the utility-first Tailwind CSS framework featuring dark mode support, a Figma design system, and more.</p><p>It includes all of the commonly used components that a website requires, such as buttons, dropdowns, navigation bars, modals, datepickers, advanced charts and the list goes on.</p><p>Here is an example of a button component:</p><code><button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-base text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Default</button></code><p>Learn more about all components from the <a href="https://flowbite.com/docs/getting-started/introduction/">Flowbite Docs</a>.</p>',
editorProps: {
attributes: {
class: 'format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none',
},
}
});
// set up custom event listeners for the buttons
document.getElementById('toggleLinkButton').addEventListener('click', () => {
const url = window.prompt('Enter image URL:', 'https://flowbite.com');
editor.chain().focus().toggleLink({ href: url }).run();
});
document.getElementById('removeLinkButton').addEventListener('click', () => {
editor.chain().focus().unsetLink().run()
});
}
})
Using images #
Use this example to learn how to add images inside of the WYSIWYG text editor and configure settings such as the image URL, image alt attribute which is important for SEO and accessibility and the image title.
<div class="w-full bg-neutral-secondary-medium border border-default-medium rounded-base">
<div class="p-2 border-b border-default-medium">
<div class="flex items-center gap-2">
<div class="flex items-center space-x-1 rtl:space-x-reverse flex-wrap">
<button id="addImageButton" type="button" data-tooltip-target="tooltip-image" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M13 10a1 1 0 0 1 1-1h.01a1 1 0 1 1 0 2H14a1 1 0 0 1-1-1Z" clip-rule="evenodd"/>
<path fill-rule="evenodd" d="M2 6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12c0 .556-.227 1.06-.593 1.422A.999.999 0 0 1 20.5 20H4a2.002 2.002 0 0 1-2-2V6Zm6.892 12 3.833-5.356-3.99-4.322a1 1 0 0 0-1.549.097L4 12.879V6h16v9.95l-3.257-3.619a1 1 0 0 0-1.557.088L11.2 18H8.892Z" clip-rule="evenodd"/>
</svg>
<span class="sr-only">Add image</span>
</button>
<div id="tooltip-image" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Add image
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button type="button" data-tooltip-target="tooltip-advanced-image" data-modal-target="advanced-image-modal" data-modal-toggle="advanced-image-modal" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M9 2.221V7H4.221a2 2 0 0 1 .365-.5L8.5 2.586A2 2 0 0 1 9 2.22ZM11 2v5a2 2 0 0 1-2 2H4v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2h-7Zm.394 9.553a1 1 0 0 0-1.817.062l-2.5 6A1 1 0 0 0 8 19h8a1 1 0 0 0 .894-1.447l-2-4A1 1 0 0 0 13.2 13.4l-.53.706-1.276-2.553ZM13 9.5a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0Z" clip-rule="evenodd"/>
</svg>
<span class="sr-only">Insert advanced image</span>
</button>
<div id="tooltip-advanced-image" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Image with settings
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
</div>
<div class="p-3 rounded-b-base bg-neutral-primary">
<label for="wysiwyg-images-example" class="sr-only">Publish post</label>
<div id="wysiwyg-images-example"class="block w-full px-0 text-sm text-body bg-neutral-primary border-0 focus:ring-0"></div>
</div>
</div>
<!-- Main modal -->
<div id="advanced-image-modal" tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
<div class="relative p-4 w-full max-w-sm max-h-full">
<!-- Modal content -->
<div class="relative bg-neutral-primary-soft border border-default rounded-base shadow-sm p-4 md:p-6">
<!-- Modal header -->
<div class="flex items-center justify-between border-b border-default pb-4 md:pb-5">
<h3 class="text-lg font-medium text-heading">
Insert advanced image
</h3>
<button type="button" class="text-body bg-transparent hover:bg-neutral-tertiary hover:text-heading rounded-base text-sm w-9 h-9 ms-auto inline-flex justify-center items-center" data-modal-hide="advanced-image-modal">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18 17.94 6M18 18 6.06 6"/></svg>
<span class="sr-only">Close modal</span>
</button>
</div>
<!-- Modal body -->
<form action="#">
<div class="grid gap-4 grid-cols-2 py-4 md:py-6">
<div class="col-span-2">
<label for="image-url" class="block mb-2.5 text-sm font-medium text-heading">Image URL</label>
<input type="text" name="image-url" id="image-url" class="bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand block w-full px-3 py-2.5 shadow-xs placeholder:text-body" placeholder="https://placehold.co/600x400" required="">
</div>
<div class="col-span-2">
<label for="image-alt" class="block mb-2.5 text-sm font-medium text-heading">Image alt</label>
<input type="text" name="image-alt" id="image-alt" class="bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand block w-full px-3 py-2.5 shadow-xs placeholder:text-body" placeholder="" required="">
</div>
<div class="col-span-2">
<label for="image-title" class="block mb-2.5 text-sm font-medium text-heading">Image title</label>
<input type="text" name="image-title" id="image-title" class="bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand block w-full px-3 py-2.5 shadow-xs placeholder:text-body" placeholder="" required="">
</div>
</div>
<button type="submit" class="w-full inline-flex items-center justify-center text-white bg-brand hover:bg-brand-strong box-border border border-transparent focus:ring-4 focus:ring-brand-medium shadow-xs font-medium leading-5 rounded-base text-sm px-4 py-2.5 focus:outline-none">
<svg class="w-4 h-4 me-1.5 -ms-0.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14m-7 7V5"/></svg>
Add image
</button>
</form>
</div>
</div>
</div>
import { Editor } from 'https://esm.sh/@tiptap/core@2.6.6';
import StarterKit from 'https://esm.sh/@tiptap/starter-kit@2.6.6';
import Image from 'https://esm.sh/@tiptap/extension-image@2.6.6';
window.addEventListener('load', function() {
if (document.getElementById("wysiwyg-images-example")) {
// tip tap editor setup
const editor = new Editor({
element: document.querySelector('#wysiwyg-images-example'),
extensions: [
StarterKit,
Image
],
content: '<p>Flowbite is an <strong>open-source library of UI components</strong> based on the utility-first Tailwind CSS framework featuring dark mode support, a Figma design system, and more.</p><img src="https://placehold.co/600x400" contenteditable="false" draggable="true"><p>It includes all of the commonly used components that a website requires, such as buttons, dropdowns, navigation bars, modals, datepickers, advanced charts and the list goes on.</p><p>Here is an example of a button component:</p><code><button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-base text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Default</button></code><p>Learn more about all components from the <a href="https://flowbite.com/docs/getting-started/introduction/">Flowbite Docs</a>.</p>',
editorProps: {
attributes: {
class: 'format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none',
},
}
});
// set up custom event listeners for the buttons
document.getElementById('addImageButton').addEventListener('click', () => {
const url = window.prompt('Enter image URL:', 'https://placehold.co/600x400');
if (url) {
editor.chain().focus().setImage({ src: url }).run();
}
});
const advancedImageModal = FlowbiteInstances.getInstance('Modal', 'advanced-image-modal');
document.getElementById('advancedImageForm').addEventListener('submit', (e) => {
e.preventDefault();
const imageUrl = document.getElementById('URL').value;
const imageAlt = document.getElementById('alt').value;
const imageTitle = document.getElementById('title').value;
if (imageUrl) {
editor.chain().focus().setImage({ src: imageUrl, alt: imageAlt ? imageAlt : '', title: imageTitle ? imageTitle: '' }).run();
document.getElementById('advancedImageForm').reset();
advancedImageModal.hide();
}
});
}
})
Adding videos #
Use this example to embed videos inside the WYSIWYG text editor based on a YouTube URL source and set the width and height of the video by using the Flowbite modal component API.
<div class="w-full bg-neutral-secondary-medium border border-default-medium rounded-base">
<div class="p-2 border-b border-default-medium">
<div class="flex items-center gap-2">
<div class="flex items-center space-x-1 rtl:space-x-reverse flex-wrap">
<button id="addVideoButton" type="button" data-tooltip-target="tooltip-video" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 6H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1Zm7 11-6-2V9l6-2v10Z"/></svg>
<span class="sr-only">Add video</span>
</button>
<div id="tooltip-video" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Add video
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button type="button" data-tooltip-target="tooltip-advanced-video" data-modal-target="advanced-video-modal" data-modal-toggle="advanced-video-modal" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linejoin="round" stroke-width="2" d="M10 3v4a1 1 0 0 1-1 1H5m14-4v16a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7.914a1 1 0 0 1 .293-.707l3.914-3.914A1 1 0 0 1 9.914 3H18a1 1 0 0 1 1 1ZM9 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1Zm5.697 2.395v-.733l1.269-1.219v2.984l-1.268-1.032Z"/></svg>
<span class="sr-only">Insert advanced video</span>
</button>
<div id="tooltip-advanced-video" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Video with settings
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
</div>
<div class="p-3 rounded-b-base bg-neutral-primary">
<label for="wysiwyg-video-example" class="sr-only">Publish post</label>
<div id="wysiwyg-video-example"class="block w-full px-0 text-sm text-body bg-neutral-primary border-0 focus:ring-0"></div>
</div>
</div>
<!-- Main modal -->
<div id="advanced-video-modal" tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
<div class="relative p-4 w-full max-w-sm max-h-full">
<!-- Modal content -->
<div class="relative bg-neutral-primary-soft border border-default rounded-base shadow-sm p-4 md:p-6">
<!-- Modal header -->
<div class="flex items-center justify-between border-b border-default pb-4 md:pb-5">
<h3 class="text-lg font-medium text-heading">
Insert advanced video
</h3>
<button type="button" class="text-body bg-transparent hover:bg-neutral-tertiary hover:text-heading rounded-base text-sm w-9 h-9 ms-auto inline-flex justify-center items-center" data-modal-hide="advanced-video-modal">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18 17.94 6M18 18 6.06 6"/></svg>
<span class="sr-only">Close modal</span>
</button>
</div>
<!-- Modal body -->
<form action="#">
<div class="grid gap-4 grid-cols-2 py-4 md:py-6">
<div class="col-span-2">
<label for="video-url" class="block mb-2.5 text-sm font-medium text-heading">Youtube URL</label>
<input type="text" name="video-url" id="video-url" class="bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand block w-full px-3 py-2.5 shadow-xs placeholder:text-body" placeholder="https://www.youtube.com/watch?v=KaLxCiilHns" required="">
</div>
<div class="col-span-2 md:col-span-1">
<label for="video-width" class="block mb-2.5 text-sm font-medium text-heading">Video width</label>
<input type="text" name="video-width" id="video-width" class="bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand block w-full px-3 py-2.5 shadow-xs placeholder:text-body" placeholder="" required="">
</div>
<div class="col-span-2 md:col-span-1">
<label for="video-height" class="block mb-2.5 text-sm font-medium text-heading">Video height</label>
<input type="text" name="video-height" id="video-height" class="bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand block w-full px-3 py-2.5 shadow-xs placeholder:text-body" placeholder="" required="">
</div>
</div>
<button type="submit" class="w-full inline-flex items-center justify-center text-white bg-brand hover:bg-brand-strong box-border border border-transparent focus:ring-4 focus:ring-brand-medium shadow-xs font-medium leading-5 rounded-base text-sm px-4 py-2.5 focus:outline-none">
<svg class="w-4 h-4 me-1.5 -ms-0.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14m-7 7V5"/></svg>
Add video
</button>
</form>
</div>
</div>
</div>
import { Editor } from 'https://esm.sh/@tiptap/core@2.6.6';
import StarterKit from 'https://esm.sh/@tiptap/starter-kit@2.6.6';
import YouTube from 'https://esm.sh/@tiptap/extension-youtube@2.6.6';
window.addEventListener('load', function() {
if (document.getElementById("wysiwyg-video-example")) {
// tip tap editor setup
const editor = new Editor({
element: document.querySelector('#wysiwyg-video-example'),
extensions: [
StarterKit,
YouTube
],
content: '<p>Flowbite is an <strong>open-source library of UI components</strong> based on the utility-first Tailwind CSS framework featuring dark mode support, a Figma design system, and more.</p><img src="https://placehold.co/600x400" contenteditable="false" draggable="true"><p>It includes all of the commonly used components that a website requires, such as buttons, dropdowns, navigation bars, modals, datepickers, advanced charts and the list goes on.</p><p>Here is an example of a button component:</p><code><button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-base text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Default</button></code><p>Learn more about all components from the <a href="https://flowbite.com/docs/getting-started/introduction/">Flowbite Docs</a>.</p>',
editorProps: {
attributes: {
class: 'format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none',
},
}
});
// set up custom event listeners for the buttons
document.getElementById('addVideoButton').addEventListener('click', () => {
const url = window.prompt('Enter YouTube URL:', 'https://www.youtube.com/watch?v=KaLxCiilHns');
if (url) {
editor.commands.setYoutubeVideo({
src: url,
width: 640,
height: 480,
})
}
});
const advancedVideoModal = FlowbiteInstances.getInstance('Modal', 'advanced-video-modal');
document.getElementById('advancedVideoForm').addEventListener('submit', (e) => {
e.preventDefault();
const videoUrl = document.getElementById('URL').value;
const videoWidth = document.getElementById('width').value;
const videoHeight = document.getElementById('height').value;
if (videoUrl) {
editor.commands.setYoutubeVideo({ src: videoUrl, width: videoWidth ? videoWidth : 640, height: videoHeight ? videoHeight: 480 });
document.getElementById('advancedVideoForm').reset();
advancedVideoModal.hide();
}
});
}
})
Editing tables #
Use this example to edit table data inside the WYSIWYG text editor by adding and removing table column, rows, and cells and use other features to navigate through the table data for a convenient editing process.
<div class="w-full bg-neutral-secondary-medium border border-default-medium rounded-base">
<div class="p-2 border-b border-default-medium">
<div class="flex items-center gap-2">
<div class="flex items-center space-x-1 rtl:space-x-reverse flex-wrap">
<button id="addTableButton" type="button" data-tooltip-target="tooltip-table" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15v3c0 .5523.44772 1 1 1h10.5M3 15v-4m0 4h11M3 11V6c0-.55228.44772-1 1-1h16c.5523 0 1 .44772 1 1v5M3 11h18m0 0v1M8 11v8m4-8v8m4-8v2m1 4h2m0 0h2m-2 0v2m0-2v-2"/>
</svg>
<span class="sr-only">Add table</span>
</button>
<div id="tooltip-table" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Add table
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="deleteTableButton" type="button" data-tooltip-target="tooltip-delete-table" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15.5v3c0 .5523.44772 1 1 1h10.5M3 15.5v-4m0 4h11m-11-4v-5c0-.55228.44772-1 1-1h16c.5523 0 1 .44772 1 1v5m-18 0h18m0 0v1m-13-1v8m4-8v8m4-8v2m1.8956 5.9528 1.5047-1.5047m0 0 1.5048-1.5048m-1.5048 1.5048 1.4605 1.4604m-1.4605-1.4604-1.4604-1.4605"/>
</svg>
<span class="sr-only">Delete table</span>
</button>
<div id="tooltip-delete-table" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Delete table
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<div class="px-1">
<span class="block w-px h-4 bg-neutral-quaternary"></span>
</div>
<button id="addColumnBeforeButton" type="button" data-tooltip-target="tooltip-add-column-before" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 5.5v14m-8-7h2m0 0h2m-2 0v2m0-2v-2m12 1h-6m6 4h-6m-11 4h16c.5523 0 1-.4477 1-1v-12c0-.55228-.4477-1-1-1H4c-.55228 0-1 .44772-1 1v12c0 .5523.44772 1 1 1Z"/>
</svg>
<span class="sr-only">Add column before</span>
</button>
<div id="tooltip-add-column-before" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Add column before
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="addColumnAfterButton" type="button" data-tooltip-target="tooltip-add-column-after" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5.5v14m8-7h-2m0 0h-2m2 0v2m0-2v-2m-12 1h6m-6 4h6m11 4H4c-.55228 0-1-.4477-1-1v-12c0-.55228.44772-1 1-1h16c.5523 0 1 .44772 1 1v12c0 .5523-.4477 1-1 1Z"/>
</svg>
<span class="sr-only">Add column after</span>
</button>
<div id="tooltip-add-column-after" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Add column after
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="removeColumnButton" type="button" data-tooltip-target="tooltip-remove-column" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5.5v14m-6-8h6m-6 4h6m4.5061-1.4939L15.0123 12.5m0 0 1.5061-1.5061M15.0123 12.5l1.5061 1.5061M15.0123 12.5l-1.5062-1.5061M20 19.5H4c-.55228 0-1-.4477-1-1v-12c0-.55228.44772-1 1-1h16c.5523 0 1 .44772 1 1v12c0 .5523-.4477 1-1 1Z"/>
</svg>
<span class="sr-only">Remove column</span>
</button>
<div id="tooltip-remove-column" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Remove column
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<div class="px-1">
<span class="block w-px h-4 bg-neutral-quaternary"></span>
</div>
<button id="addRowBeforeButton" type="button" data-tooltip-target="tooltip-add-row-before" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15.5v3c0 .5523.44772 1 1 1h16c.5523 0 1-.4477 1-1v-3m-18 0v-9c0-.55228.44772-1 1-1h16c.5523 0 1 .44772 1 1v9m-18 0h18m-13 0v4m4-4v4m4-4v4m-6-9h2m0 0h2m-2 0v2m0-2v-2"/>
</svg>
<span class="sr-only">Add row before</span>
</button>
<div id="tooltip-add-row-before" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Add row before
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="addRowAfterButton" type="button" data-tooltip-target="tooltip-add-row-after" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9.5v-3c0-.55228.44772-1 1-1h16c.5523 0 1 .44771 1 1v3m-18 0v9c0 .5523.44772 1 1 1h16c.5523 0 1-.4477 1-1v-9m-18 0h18m-13 0v-4m4 4v-4m4 4v-4m-6 9h2m0 0h2m-2 0v-2m0 2v2"/>
</svg>
<span class="sr-only">Add row after</span>
</button>
<div id="tooltip-add-row-after" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Add row after
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="removeRowButton" type="button" data-tooltip-target="tooltip-remove-row" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15.5v3c0 .5523.44772 1 1 1h16c.5523 0 1-.4477 1-1v-3m-18 0v-9c0-.55228.44772-1 1-1h16c.5523 0 1 .44772 1 1v9m-18 0h18m-13 0v4m4-4v4m4-4v4m-5.5061-7.4939L12 10.5m0 0 1.5061-1.50614M12 10.5l1.5061 1.5061M12 10.5l-1.5061-1.50614"/>
</svg>
<span class="sr-only">Remove row</span>
</button>
<div id="tooltip-remove-row" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Remove row
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
<div class="flex items-center space-x-1 rtl:space-x-reverse flex-wrap">
<button id="mergeCellsButton" type="button" data-tooltip-target="tooltip-merge-cells" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 18.5v2H4v-16h6v2m4 12v2h6v-16h-6v2m-6.49543 8.4954L10 12.5m0 0-2.49543-2.4954M10 12.5H4.05191m12.50199 2.5539L14 12.5m0 0 2.5539-2.55392M14 12.5h5.8319"/>
</svg>
<span class="sr-only">Merge cells</span>
</button>
<div id="tooltip-merge-cells" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Merge cells
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="splitCellsButton" type="button" data-tooltip-target="tooltip-split-cells" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 18.5v2h6v-16H4v2m16 12v2h-6v-16h6v2M6.49545 14.9954 4.00003 12.5m0 0 2.49542-2.4954M4.00003 12.5h5.94809m7.49798 2.5539L20 12.5m0 0-2.5539-2.55392M20 12.5h-5.8319"/>
</svg>
<span class="sr-only">Split cells</span>
</button>
<div id="tooltip-split-cells" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Split cells
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="mergeOrSplitButton" type="button" data-tooltip-target="tooltip-merge-or-split" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.5045 14.9954 21 12.5m0 0-2.4955-2.4954M21 12.5h-5.9481m-9.49798 2.5539L3 12.5m0 0 2.55392-2.55392M3 12.5h5.83192m.16807 7v-14H15v14H8.99999Z"/>
</svg>
<span class="sr-only">Merge or split</span>
</button>
<div id="tooltip-merge-or-split" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Merge or split
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<div class="px-1">
<span class="block w-px h-4 bg-neutral-quaternary"></span>
</div>
<button id="toggleHeaderColumnButton" type="button" data-tooltip-target="tooltip-toggle-header-column" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 5.5v14m6-8h-6m6 4h-6m-9-3h1.99093M4 19.5h16c.5523 0 1-.4477 1-1v-12c0-.55228-.4477-1-1-1H4c-.55228 0-1 .44772-1 1v12c0 .5523.44772 1 1 1Zm8-7c0 1.1046-.8954 2-2 2-1.10457 0-2-.8954-2-2s.89543-2 2-2c1.1046 0 2 .8954 2 2Z"/>
</svg>
<span class="sr-only">Toggle header column</span>
</button>
<div id="tooltip-toggle-header-column" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle header column
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleHeaderRowButton" type="button" data-tooltip-target="tooltip-toggle-header-row" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15.5v3c0 .5523.44772 1 1 1h16c.5523 0 1-.4477 1-1v-3m-18 0v-9c0-.55228.44772-1 1-1h16c.5523 0 1 .44772 1 1v9m-18 0h18m-13 0v4m4-4v4m4-4v4m-7-9h1.9909M15 10.5c0 1.1046-.8954 2-2 2s-2-.8954-2-2c0-1.10457.8954-2 2-2s2 .89543 2 2Z"/>
</svg>
<span class="sr-only">Toggle header row</span>
</button>
<div id="tooltip-toggle-header-row" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle header row
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleHeaderCellButton" type="button" data-tooltip-target="tooltip-toggle-header-cell" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15.5v3c0 .5523.44772 1 1 1h16c.5523 0 1-.4477 1-1v-3m-18 0v-9c0-.55228.44772-1 1-1h16c.5523 0 1 .44772 1 1v9m-18 0h18m-13 0v4m4-4v4m4-4v4m-7-9h1.9909M15 10.5c0 1.1046-.8954 2-2 2s-2-.8954-2-2c0-1.10457.8954-2 2-2s2 .89543 2 2Z"/>
</svg>
<span class="sr-only">Toggle header cell</span>
</button>
<div id="tooltip-toggle-header-cell" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Toggle header cell
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button data-modal-toggle="cell-attribute-modal" data-modal-target="cell-attribute-modal" type="button" data-tooltip-target="tooltip-add-cell-attribute" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15.5v3c0 .5523.44772 1 1 1h8v-8m-9 4v-4m0 4h9m-9-4v-5c0-.55228.44772-1 1-1h16c.5523 0 1 .44772 1 1v4m-18 1h11m6.25 5c0 1.2426-1.0073 2.25-2.25 2.25m2.25-2.25c0-1.2426-1.0073-2.25-2.25-2.25m2.25 2.25H21m-3 2.25c-1.2426 0-2.25-1.0074-2.25-2.25M18 18.75v.75m-2.25-3c0-1.2426 1.0074-2.25 2.25-2.25m-2.25 2.25H15m3-2.25v-.75m-1.591 1.409-.5303-.5303m4.2426 4.2426-.5303-.5303m-3.182 0-.5303.5303m4.2426-4.2426-.5303.5303"/>
</svg>
<span class="sr-only">Add cell attribute</span>
</button>
<div id="tooltip-add-cell-attribute" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Add cell attribute
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<div class="px-1">
<span class="block w-px h-4 bg-neutral-tertiary"></span>
</div>
<button id="fixTablesButton" type="button" data-tooltip-target="tooltip-fix-tables" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15.5v3c0 .5523.44772 1 1 1h4v-4m-5 0v-4m0 4h5m-5-4v-5c0-.55228.44772-1 1-1h16c.5523 0 1 .44772 1 1v1.98935M3 11.5h5v4m9.4708 4.1718-.8696-1.4388-2.8164-.235-2.573-4.2573 1.4873-2.8362 1.4441 2.3893c.3865.6396 1.2183.8447 1.8579.4582.6396-.3866.8447-1.2184.4582-1.858l-1.444-2.38925h3.1353l2.6101 4.27715-1.0713 2.5847.8695 1.4388"/>
</svg>
<span class="sr-only">Fix tables</span>
</button>
<div id="tooltip-fix-tables" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Fix tables
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="previousCellButton" type="button" data-tooltip-target="tooltip-previous-cell" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15.5v3c0 .5523.44772 1 1 1h9.5M3 15.5v-4m0 4h9m-9-4v-5c0-.55228.44772-1 1-1h16c.5523 0 1 .44772 1 1v5H3Zm5 0v8m4-8v8m5.9001-1.0999L16 16.5m0 0 1.9001-1.9001M16 16.5h5"/>
</svg>
<span class="sr-only">Previous cell</span>
</button>
<div id="tooltip-previous-cell" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Previous cell
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="nextCellButton" type="button" data-tooltip-target="tooltip-next-cell" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 15.5v3c0 .5523.44772 1 1 1h9.5M3 15.5v-4m0 4h9m-9-4v-5c0-.55228.44772-1 1-1h16c.5523 0 1 .44772 1 1v5H3Zm5 0v8m4-8v8m7.0999-1.0999L21 16.5m0 0-1.9001-1.9001M21 16.5h-5"/>
</svg>
<span class="sr-only">Next cell</span>
</button>
<div id="tooltip-next-cell" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Next cell
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
<div class="p-3 rounded-b-base bg-neutral-primary">
<label for="wysiwyg-tables-example" class="sr-only">Publish post</label>
<div id="wysiwyg-tables-example"class="block w-full px-0 text-sm text-body bg-neutral-primary border-0 focus:ring-0"></div>
</div>
</div>
<!-- Main modal -->
<div id="cell-attribute-modal" tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
<div class="relative p-4 w-full max-w-sm max-h-full">
<!-- Modal content -->
<div class="relative bg-neutral-primary-soft border border-default rounded-base shadow-sm p-4 md:p-6">
<!-- Modal header -->
<div class="flex items-center justify-between border-b border-default pb-4">
<h3 class="text-lg font-medium text-heading">
Add cell attribute
</h3>
<button type="button" class="text-body bg-transparent hover:bg-neutral-tertiary hover:text-heading rounded-base text-sm w-9 h-9 ms-auto inline-flex justify-center items-center" data-modal-hide="cell-attribute-modal">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18 17.94 6M18 18 6.06 6"/></svg>
<span class="sr-only">Close modal</span>
</button>
</div>
<!-- Modal body -->
<form action="#">
<div class="grid gap-4 grid-cols-2 py-4 md:py-6">
<div class="col-span-2">
<label for="attribute-name" class="block mb-2.5 text-sm font-medium text-heading">Attribute name</label>
<input type="text" name="attribute-name" id="attribute-name" class="bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand block w-full px-3 py-2.5 shadow-xs placeholder:text-body" placeholder="backgroundColor" required="">
</div>
<div class="col-span-2">
<label for="attribute-value" class="block mb-2.5 text-sm font-medium text-heading">Attribute value</label>
<input type="text" name="attribute-value" id="attribute-value" class="bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand block w-full px-3 py-2.5 shadow-xs placeholder:text-body" placeholder="#F1FF3F" required="">
</div>
</div>
<button type="submit" class="w-full inline-flex items-center justify-center text-white bg-brand hover:bg-brand-strong box-border border border-transparent focus:ring-4 focus:ring-brand-medium shadow-xs font-medium leading-5 rounded-base text-sm px-4 py-2.5 focus:outline-none">
<svg class="w-4 h-4 me-1.5 -ms-0.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14m-7 7V5"/></svg>
Set attribute
</button>
</form>
</div>
</div>
</div>
import { Editor } from 'https://esm.sh/@tiptap/core@2.6.6';
import StarterKit from 'https://esm.sh/@tiptap/starter-kit@2.6.6';
import Table from 'https://esm.sh/@tiptap/extension-table@2.6.6';
import TableCell from 'https://esm.sh/@tiptap/extension-table-cell@2.6.6';
import TableHeader from 'https://esm.sh/@tiptap/extension-table-header@2.6.6';
import TableRow from 'https://esm.sh/@tiptap/extension-table-row@2.6.6';
const TipTapExtensionTableCell = TableCell.extend({
addAttributes() {
return {
...this.parent?.(),
backgroundColor: {
default: null,
renderHTML: (attributes) => {
if (!attributes.backgroundColor) {
return {}
}
return {
style: 'background-color: ' + attributes.backgroundColor,
}
},
parseHTML: (element) => {
return element.style.backgroundColor.replace(/['"]+/g, '')
},
},
}
},
});
window.addEventListener('load', function() {
if (document.getElementById("wysiwyg-tables-example")) {
// tip tap editor setup
const editor = new Editor({
element: document.querySelector('#wysiwyg-tables-example'),
extensions: [
StarterKit,
Table.configure({
resizable: true,
}),
TableRow,
TableHeader,
TableCell,
TipTapExtensionTableCell
],
content: '<p>Understanding global <strong>population growth trends</strong> is essential for analyzing the development and future of nations. Population growth rates provide insights into economic prospects, resource allocation, and potential challenges for countries worldwide.</p><p>Here is an example of population data:</p><div class=tableWrapper><table style=min-width:75px><col><col><col><tr><th colspan=1 rowspan=1><p>Country<th colspan=1 rowspan=1><p>Population<th colspan=1 rowspan=1><p>Growth rate<tr><td colspan=1 rowspan=1><p>United States<td colspan=1 rowspan=1><p>333 million<td colspan=1 rowspan=1><p>0.4%<tr><td colspan=1 rowspan=1><p>China<td colspan=1 rowspan=1><p>1.41 billion<td colspan=1 rowspan=1><p>0%<tr><td colspan=1 rowspan=1><p>Germany<td colspan=1 rowspan=1><p>83.8 million<td colspan=1 rowspan=1><p>0.7%<tr><td colspan=1 rowspan=1><p>India<td colspan=1 rowspan=1><p>1.42 billion<td colspan=1 rowspan=1><p>1.0%<tr><td colspan=1 rowspan=1><p>Brazil<td colspan=1 rowspan=1><p>214 million<td colspan=1 rowspan=1><p>0.6%<tr><td colspan=1 rowspan=1><p>Indonesia<td colspan=1 rowspan=1><p>273 million<td colspan=1 rowspan=1><p>1.1%<tr><td colspan=1 rowspan=1><p>Pakistan<td colspan=1 rowspan=1><p>231 million<td colspan=1 rowspan=1><p>2.0%<tr><td colspan=1 rowspan=1><p>Nigeria<td colspan=1 rowspan=1><p>223 million<td colspan=1 rowspan=1><p>2.5%</table></div><p>Learn more about global population trends from reliable sources like the <a href=https://www.worldpopulationreview.com>World Population Review</a>.</p>',
editorProps: {
attributes: {
class: 'format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none',
},
}
});
// set up custom event listeners for the buttons
document.getElementById('addTableButton').addEventListener('click', () => {
editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run();
});
// add column before
document.getElementById('addColumnBeforeButton').addEventListener('click', () => {
editor.chain().focus().addColumnBefore().run();
});
// add column after
document.getElementById('addColumnAfterButton').addEventListener('click', () => {
editor.chain().focus().addColumnAfter().run();
});
// remove column
document.getElementById('removeColumnButton').addEventListener('click', () => {
editor.chain().focus().deleteColumn().run();
});
// add row before
document.getElementById('addRowBeforeButton').addEventListener('click', () => {
editor.chain().focus().addRowBefore().run();
});
// add row after
document.getElementById('addRowAfterButton').addEventListener('click', () => {
editor.chain().focus().addRowAfter().run();
});
// remove row
document.getElementById('removeRowButton').addEventListener('click', () => {
editor.chain().focus().deleteRow().run();
});
// delete table
document.getElementById('deleteTableButton').addEventListener('click', () => {
editor.chain().focus().deleteTable().run();
});
// merge cells
document.getElementById('mergeCellsButton').addEventListener('click', () => {
editor.chain().focus().mergeCells().run();
});
// split cells
document.getElementById('splitCellsButton').addEventListener('click', () => {
editor.chain().focus().splitCell().run();
});
// merge or split
document.getElementById('mergeOrSplitButton').addEventListener('click', () => {
editor.chain().focus().mergeOrSplit().run();
});
// toggle header column
document.getElementById('toggleHeaderColumnButton').addEventListener('click', () => {
editor.chain().focus().toggleHeaderColumn().run();
});
// toggle header row
document.getElementById('toggleHeaderRowButton').addEventListener('click', () => {
editor.chain().focus().toggleHeaderRow().run();
});
// toggle header cell
document.getElementById('toggleHeaderCellButton').addEventListener('click', () => {
editor.chain().focus().toggleHeaderCell().run();
});
const cellAttributeModal = FlowbiteInstances.getInstance('Modal', 'cell-attribute-modal');
document.getElementById('addCellAttributeForm').addEventListener('submit', (e) => {
e.preventDefault();
const attributeName = document.getElementById('attribute-name').value;
const attributeValue = document.getElementById('attribute-value').value;
if (attributeName && attributeValue) {
const result = editor.commands.setCellAttribute(attributeName ? attributeName : '', attributeValue ? attributeValue : '');
document.getElementById('addCellAttributeForm').reset();
cellAttributeModal.hide();
}
});
// fix tables
document.getElementById('fixTablesButton').addEventListener('click', () => {
editor.commands.fixTables();
});
// go to previous cell
document.getElementById('previousCellButton').addEventListener('click', () => {
editor.chain().focus().goToPreviousCell().run();
});
// go to the next cell
document.getElementById('nextCellButton').addEventListener('click', () => {
editor.chain().focus().goToNextCell().run();
});
}
})
Undo and redo #
Use the history functionality from the WYSIWYG text editor component to integrate undo and redo actions.
<div class="w-full bg-neutral-secondary-medium border border-default-medium rounded-base">
<div class="p-2 border-b border-default-medium">
<div class="flex flex-wrap items-center">
<div class="flex items-center space-x-1 rtl:space-x-reverse flex-wrap">
<button id="toggleUndoButton" data-tooltip-target="tooltip-undo" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9h13a5 5 0 0 1 0 10H7M3 9l4-4M3 9l4 4"/>
</svg>
<span class="sr-only">Undo</span>
</button>
<div id="tooltip-undo" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Undo
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleRedoButton" data-tooltip-target="tooltip-redo" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 9H8a5 5 0 0 0 0 10h9m4-10-4-4m4 4-4 4"/>
</svg>
<span class="sr-only">Redo</span>
</button>
<div id="tooltip-redo" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Redo
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
</div>
<div class="p-3 rounded-b-base bg-neutral-primary">
<label for="wysiwyg-history-example" class="sr-only">Publish post</label>
<div id="wysiwyg-history-example"class="block w-full px-0 text-sm text-body bg-neutral-primary border-0 focus:ring-0"></div>
</div>
</div>
import { Editor } from 'https://esm.sh/@tiptap/core@2.6.6';
import StarterKit from 'https://esm.sh/@tiptap/starter-kit@2.6.6';
window.addEventListener('load', function() {
if (document.getElementById("wysiwyg-history-example")) {
// tip tap editor setup
const editor = new Editor({
element: document.querySelector('#wysiwyg-history-example'),
extensions: [
StarterKit
],
content: '<p>Flowbite is an <strong>open-source library of UI components</strong> based on the utility-first Tailwind CSS framework featuring dark mode support, a Figma design system, and more.</p><p>It includes all of the commonly used components that a website requires, such as buttons, dropdowns, navigation bars, modals, datepickers, advanced charts and the list goes on.</p><p>Here is an example of a button component:</p><code><button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-base text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Default</button></code><p>Learn more about all components from the <a href="https://flowbite.com/docs/getting-started/introduction/">Flowbite Docs</a>.</p>',
editorProps: {
attributes: {
class: 'format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none',
},
}
});
// set up custom event listeners for the buttons
document.getElementById('toggleUndoButton').addEventListener('click', () => {
editor.chain().focus().undo().run();
});
document.getElementById('toggleRedoButton').addEventListener('click', () => {
editor.chain().focus().redo().run()
});
}
})
Exporting data #
Use the editor.getJSON() and the editor.getHTML() functions to export the text content inside of the WYSIWYG text editor in JSON or raw HTML format to persist into your database or API structure.
<div class="w-full bg-neutral-secondary-medium border border-default-medium rounded-base">
<div class="p-2 border-b border-default-medium">
<div class="flex flex-wrap items-center">
<div class="flex items-center space-x-1 rtl:space-x-reverse flex-wrap">
<button id="toggleJSONButton" data-tooltip-target="tooltip-json" data-modal-target="source-code-modal" data-modal-toggle="source-code-modal" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 8v8m0-8a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm0 8a2 2 0 1 0 0 4 2 2 0 0 0 0-4Zm6-2a2 2 0 1 1 4 0 2 2 0 0 1-4 0Zm0 0h-1a5 5 0 0 1-5-5v-.5"/>
</svg>
<span class="sr-only">JSON</span>
</button>
<div id="tooltip-json" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Get JSON
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
<button id="toggleHTMLButton" data-tooltip-target="tooltip-html" data-modal-target="source-code-modal" data-modal-toggle="source-code-modal" type="button" class="p-1.5 text-body rounded-sm cursor-pointer hover:text-heading hover:bg-neutral-quaternary">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path d="m3 2 1.578 17.824L12 22l7.467-2.175L21 2H3Zm14.049 6.048H9.075l.172 2.016h7.697l-.626 6.565-4.246 1.381-4.281-1.455-.288-2.932h2.024l.16 1.411 2.4.815 2.346-.763.297-3.005H7.416l-.562-6.05h10.412l-.217 2.017Z"/>
</svg>
<span class="sr-only">HTML</span>
</button>
<div id="tooltip-html" role="tooltip" class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-dark-strong rounded-base shadow-xs opacity-0 tooltip">
Get HTML
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
</div>
<div class="p-3 rounded-b-base bg-neutral-primary">
<label for="wysiwyg-export-example" class="sr-only">Publish post</label>
<div id="wysiwyg-export-example"class="block w-full px-0 text-sm text-body bg-neutral-primary border-0 focus:ring-0"></div>
</div>
</div>
<div id="source-code-modal" tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
<div class="relative p-4 w-full max-w-xl max-h-full">
<!-- Modal content -->
<div class="relative rounded-base shadow-sm bg-neutral-primary-soft">
<!-- Modal header -->
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t border-default">
<h3 class="text-lg font-semibold text-heading">
JSON/HTML data export result
</h3>
<button type="button" class="text-body bg-transparent hover:bg-neutral-tertiary hover:text-heading rounded-base text-sm w-9 h-9 ms-auto inline-flex justify-center items-center" data-modal-hide="source-code-modal">
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
</svg>
<span class="sr-only">Close modal</span>
</button>
</div>
<!-- Modal body -->
<div class="px-4 pb-4 md:px-5 pb-5 format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none">
<pre class="bg-neutral-primary-medium"><code id="sourceCode"></code></pre>
</div>
</div>
</div>
</div>
import { Editor } from 'https://esm.sh/@tiptap/core@2.6.6';
import StarterKit from 'https://esm.sh/@tiptap/starter-kit@2.6.6';
window.addEventListener('load', function() {
if (document.getElementById("wysiwyg-export-example")) {
// tip tap editor setup
const editor = new Editor({
element: document.querySelector('#wysiwyg-export-example'),
extensions: [
StarterKit
],
content: '<p>Flowbite is an <strong>open-source library of UI components</strong> based on the utility-first Tailwind CSS framework featuring dark mode support, a Figma design system, and more.</p><p>It includes all of the commonly used components that a website requires, such as buttons, dropdowns, navigation bars, modals, datepickers, advanced charts and the list goes on.</p><p>Here is an example of a button component:</p><code><button type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-base text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Default</button></code><p>Learn more about all components from the <a href="https://flowbite.com/docs/getting-started/introduction/">Flowbite Docs</a>.</p>',
editorProps: {
attributes: {
class: 'format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none',
},
}
});
const sourceCodeModal = FlowbiteInstances.getInstance('Modal', 'source-code-modal');
const sourceCodeWrapper = document.getElementById('sourceCode');
// set up custom event listeners for the buttons
document.getElementById('toggleHTMLButton').addEventListener('click', () => {
// basically just use editor.getHTML(); to get the raw html
sourceCodeWrapper.innerHTML = editor.getHTML()
.replace(/&/g, "&") // Escape & character
.replace(/</g, "<") // Escape < character
.replace(/>/g, ">") // Escape > character
.replace(/"/g, """) // Escape " character
.replace(/'/g, "'"); // Escape ' character
});
document.getElementById('toggleJSONButton').addEventListener('click', () => {
// basically just use editor.getJSON(); to get the raw json
sourceCode.innerHTML = JSON.stringify(editor.getJSON(), null, 2)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
});
}
})
Javascript behaviour #
Learn more about how you can programmatically use the WYSIWYG editor using Javascript by creating a new instance of the object, setting options, method, event listeners, and more to integrate with your code base.
After you have installed Tip Tap via NPM or CDN you can create a new Editor object:
- wysiwyg.js
import { Editor } from '@tiptap/core';
import StarterKit from '@tiptap/starter-kit';
new Editor({
element: document.getElementById('wysiwyg'),
extensions: [StarterKit],
content: '<p>Welcome to Flowbite!</p>',
})
Make sure that you also have an empty div element with the appropiate ID:
- wysiwyg.html
<div id="wysiwyg"></div>
This code will automatically set up the markup needed inside of the WYSIWYG component. Please note the fact that the Tip Tap library is headless so you need to style the elements yourself, but you can copy-paste the examples from Flowbite on this page.
Content styling #
We also recommend adding custom typography classes from the Flowbite Typography package so that the content inside of the text editor will be correctly styled:
- wysiwyg.js
new Editor({
element: document.getElementById('wysiwyg'),
extensions: [StarterKit],
content: '<p>Welcome to Flowbite!</p>',
editorProps: {
attributes: {
class: 'format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none',
},
}
})
Extensions #
Tip Tap is a modular library meaning that if you want to introduce images, videos, links and other elements inside the WYSIWYG component you need to specifically import that resources from the library and set it as an extension when initialising the Editor object.
Here is one example where we add the link extension:
- wysiwyg.js
import { Editor } from '@tiptap/core';
import StarterKit from '@tiptap/starter-kit';
import Link from '@tiptap/extension-link';
const editor = new Editor({
element: document.querySelector('#wysiwyg-links-example'),
extensions: [
StarterKit,
Link.configure({
openOnClick: false,
autolink: true,
defaultProtocol: 'https',
})
],
content: '<p>Flowbite is an <strong>open-source library of UI components</strong> based on the utility-first Tailwind CSS framework featuring dark mode support, a Figma design system, and more.</p><p>It includes all of the commonly used components that a website requires, such as buttons, dropdowns, navigation bars, modals, datepickers, advanced charts and the list goes on.</p><p>Learn more about all components from the <a href="https://flowbite.com/docs/getting-started/introduction/">Flowbite Docs</a>.</p>',
editorProps: {
attributes: {
class: 'format lg:format-lg dark:format-invert focus:outline-none format-blue max-w-none',
},
}
});
Links will now be available inside the WYSIWYG component. Learn more about all of the extensions API.
Methods #
You can easily call the methods from the Editor object to set text styles, links, images, and more. Here is one example where based upon a click event on a button you will be prompted with the URL of the link and it will add it to the currently selected text:
- wysiwyg.js
// set up custom event listeners for the buttons
document.getElementById('toggleLinkButton').addEventListener('click', () => {
const url = window.prompt('Enter image URL:', 'https://flowbite.com');
editor.chain().focus().toggleLink({ href: url }).run();
});
And here’s another example where you can unset a link:
- wysiwyg.js
// unset the links based on a button click
document.getElementById('removeLinkButton').addEventListener('click', () => {
editor.chain().focus().unsetLink().run()
});
Examples from this page have functional elements so you can check the JavaScript tab for the source code.
License #
Resources from this page are licensed under the MIT License, including the Flowbite examples and Tip Tap.