Fluxo UIFluxo UIv0.4.1

HTML Editor & Preview

A WYSIWYG rich text editor with full formatting — inline marks, colors, fonts, alignment, lists, tables, images, and a safe HTML preview renderer. Theme-aware and accessible by default.

Basic Usage

Rich Text Editor

Full WYSIWYG editing with inline, block, color, font, alignment, list, table, and link/image support.

HTML Rich Text Editor

A WYSIWYG editor with the full range of rich formatting: underline, strikethrough, inline code, highlighted text, superscript, subscript.

Text colors & fonts

You can change text color, background color, font size, and font family on any selection.

Lists

  • Nested unordered lists
  • Items with inline formatting
    • Even deeper
    • Multiple levels supported
  1. Ordered
  2. Numbered
  3. Sequences
  • Task lists with checkboxes
  • Click the box to toggle

Alignment

Left aligned paragraph.

Center aligned paragraph.

Right aligned paragraph.

Justified paragraph that stretches across the full width when there is enough content to demonstrate the justification behavior of text blocks.

Blockquote

"WYSIWYG means what you see is what you get." — every rich text editor ever.

Code block

const editor = new HtmlEditor();
editor.mount('#root');

Table

FeatureInlineBlock
Headingsyes
Colorsyesyes
Tablesyes

Try any of the toolbar actions or keyboard shortcuts — Ctrl+B, Ctrl+I, Ctrl+U, Ctrl+K, Ctrl+Alt+1..6.

HTML Rich Text Editor

A WYSIWYG editor with the full range of rich formatting: underline, strikethrough, inline code, highlighted text, superscript, subscript.

Text colors & fonts

You can change text color, background color, font size, and font family on any selection.

Lists

  • Nested unordered lists
  • Items with inline formatting
    • Even deeper
    • Multiple levels supported
  1. Ordered
  2. Numbered
  3. Sequences
  • Task lists with checkboxes
  • Click the box to toggle

Alignment

Left aligned paragraph.

Center aligned paragraph.

Right aligned paragraph.

Justified paragraph that stretches across the full width when there is enough content to demonstrate the justification behavior of text blocks.

Blockquote

"WYSIWYG means what you see is what you get." — every rich text editor ever.

Code block

const editor = new HtmlEditor();
editor.mount('#root');

Table

FeatureInlineBlock
Headingsyes
Colorsyesyes
Tablesyes

Try any of the toolbar actions or keyboard shortcuts — Ctrl+B, Ctrl+I, Ctrl+U, Ctrl+K, Ctrl+Alt+1..6.

141 words · 1061 chars
import { HtmlEditor } from 'fluxo-ui';

const [value, setValue] = useState('<h1>Hello</h1>');

<HtmlEditor
  value={value}
  onChange={setValue}
  defaultView="split"
  maxHeight={520}
/>

View Modes

Edit / Split / Preview

Fully controlled view mode — wire it to your own UI if you want external toggles.

View Modes

Switch between Edit, Split, and Preview from the top-right toolbar.

Sample content

Inline: bold, italic, strike, inline code, and a link.

  • Edit-only hides the preview pane
  • Split shows both side-by-side (tabs on mobile)
  • Preview hides the editor entirely

Each view can be controlled externally via the view prop.

View Modes

Switch between Edit, Split, and Preview from the top-right toolbar.

Sample content

Inline: bold, italic, strike, inline code, and a link.

  • Edit-only hides the preview pane
  • Split shows both side-by-side (tabs on mobile)
  • Preview hides the editor entirely

Each view can be controlled externally via the view prop.

50 words · 334 chars
Current view: split
const [view, setView] = useState<EditorViewMode>('split');

<HtmlEditor
  value={value}
  onChange={setValue}
  view={view}
  onViewChange={setView}
/>

Preview Only

Preview Only

Render sanitized HTML without the editor — ideal for displaying user-generated content.

Preview-only Renderer

Use HtmlPreview when you only need to render sanitized HTML — comments, blog posts, or read-only docs.

What this renderer supports

Inline: bold, italic, underline, strike, code, highlight, sup, sub, and links.

Colors: blue, red, highlighted.

Lists

  1. First top-level item
  2. Second with nested list
    • Nested bullet
    • Another nested bullet

Blockquote

Blockquotes can span multiple lines and contain inline formatting.

Code block

import { HtmlPreview } from 'fluxo-ui';
<HtmlPreview value={html} />

Table

LeftCenterRight
onetwothree
shortlongtext

Dangerous scripts and javascript: URLs are automatically stripped.

Input: &lt;p>Safe paragraph&lt;/p>&lt;script>alert('xss')&lt;/script>&lt;p>&lt;a href="javascript:alert(1)">click&lt;/a>&lt;/p>&lt;p onclick="alert(1)">event handler attempt&lt;/p>

Safe paragraph

click

event handler attempt

Scripts and unsafe URLs were removed automatically.
import { HtmlPreview } from 'fluxo-ui';

<HtmlPreview value={html} openLinksInNewTab />

Custom Toolbar

Minimal Toolbar

Use the built-in minimal preset for simple comment boxes.

Custom Toolbars

Configure exactly which formatting buttons appear. Pass toolbar with any subset of actions, or use the built-in MINIMAL_HTML_TOOLBAR.

Quotes still work even when the toolbar is hidden — all keyboard shortcuts are always available.

43 words · 301 chars

Custom Selection

Pass an explicit list of toolbar actions in your preferred order.

Custom Toolbars

Configure exactly which formatting buttons appear. Pass toolbar with any subset of actions, or use the built-in MINIMAL_HTML_TOOLBAR.

Quotes still work even when the toolbar is hidden — all keyboard shortcuts are always available.

43 words · 301 chars

No Toolbar

Disable the toolbar entirely — keyboard shortcuts still work.

Custom Toolbars

Configure exactly which formatting buttons appear. Pass toolbar with any subset of actions, or use the built-in MINIMAL_HTML_TOOLBAR.

Quotes still work even when the toolbar is hidden — all keyboard shortcuts are always available.

43 words · 301 chars
import { HtmlEditor, MINIMAL_HTML_TOOLBAR } from 'fluxo-ui';

<HtmlEditor toolbar={MINIMAL_HTML_TOOLBAR} />

<HtmlEditor
  toolbar={['bold', 'italic', 'underline', 'divider', 'h2', 'h3', 'divider', 'textColor', 'link', 'quote']}
/>

<HtmlEditor toolbar={false} />

Image Upload — Immediate

Immediate Upload Strategy

Every selected image uploads right away and the final URL is inserted into the HTML.

Immediate Upload

Click the image button in the toolbar, or paste / drop an image directly into the editor.

The editor calls your uploadImage callback immediately and inserts the final URL once it resolves.

Things to try

  1. Click the image toolbar button and choose Upload
  2. Paste an image from your clipboard
  3. Drag an image file from your desktop onto the editor

A valley at dawn

Already-hosted images work too — paste the URL via the From URL tab in the image dialog.

78 words · 460 chars
<HtmlEditor
  value={value}
  onChange={setValue}
  uploadStrategy="immediate"
  uploadImage={async (file) => {
    const url = await myUploader(file);
    return url;
  }}
  maxImageSize={5 * 1024 * 1024}
  acceptedImageTypes={['image/png', 'image/jpeg', 'image/webp']}
/>

Image Upload — Deferred

Deferred Upload Strategy

Images are inserted as blob URLs immediately for instant preview, then uploaded only when you flush.

Deferred Upload (Flush on Submit)

Drop or paste images — they appear instantly via local blob: URLs. When you click Submit, the editor flushes all pending uploads via flushUploads() and replaces the blob URLs with the real ones.

Workflow

  1. Drop or paste any image into the editor
  2. Keep editing — images show up immediately
  3. Click Submit to run all uploads and get the final HTML

Pre-uploaded image

Use this strategy when you don't want to upload until the user actually commits their content.

81 words · 484 chars
const editorRef = useRef<HtmlEditorHandle>(null);

<HtmlEditor
  ref={editorRef}
  value={value}
  onChange={setValue}
  uploadStrategy="deferred"
  uploadImage={async (file) => await myUploader(file)}
/>

<Button
  onClick={async () => {
    const finalHtml = await editorRef.current?.flushUploads();
    submitForm(finalHtml);
  }}
>
  Submit
</Button>

Read-only

Read-only Editor

All toolbar actions and keyboard shortcuts are disabled.

Read-only Mode

The editor can be rendered in read-only mode — content remains selectable, but the toolbar is disabled.

What you can see

Inline formatting still renders: bold, italic, underline, strike, inline code, and links.

  • Useful for displaying the editor with its chrome intact
  • Prevents any formatting changes
  • Still allows copy to clipboard

Read-only doesn't mean "hidden". It means "visible but immutable."

Read-only Mode

The editor can be rendered in read-only mode — content remains selectable, but the toolbar is disabled.

What you can see

Inline formatting still renders: bold, italic, underline, strike, inline code, and links.

  • Useful for displaying the editor with its chrome intact
  • Prevents any formatting changes
  • Still allows copy to clipboard

Read-only doesn't mean "hidden". It means "visible but immutable."

62 words · 425 chars
<HtmlEditor value={value} readOnly defaultView="split" />

Import

import { HtmlEditor, HtmlPreview } from 'fluxo-ui';
import type { HtmlEditorProps, HtmlEditorHandle, HtmlPreviewProps } from 'fluxo-ui';

HtmlEditor Props

value
string

Controlled HTML value.

defaultValue
string

Initial value when uncontrolled.

onChange
(value: string) => void

Called whenever the HTML changes.

placeholder
string"'Start writing...'"

Placeholder shown when empty.

readOnly
boolean"false"

Disable all editing while keeping the chrome.

disabled
boolean"false"

Fully disable the editor.

minHeight
string | number"'320px'"

Minimum height of the editor body.

maxHeight
string | number

Optional max height (scrolls beyond).

view
'edit' | 'split' | 'preview'

Controlled view mode.

defaultView
'edit' | 'split' | 'preview'"'edit'"

Initial view when uncontrolled.

onViewChange
(view: EditorViewMode) => void

Called when the user toggles views.

allowedViews
EditorViewMode[]

Restrict which view modes appear in the switcher.

toolbar
HtmlToolbarItem[] | false"DEFAULT_HTML_TOOLBAR"

Toolbar configuration or false to hide.

showToolbar
boolean"true"

Hide the toolbar entirely.

showStatusBar
boolean"true"

Show word/char count footer.

showWordCount
boolean"true"

Toggle word count in the status bar.

uploadImage
(file: File) => Promise<string>

Async upload callback — must resolve with the final URL.

uploadStrategy
'immediate' | 'deferred'"'immediate'"

Upload immediately or defer to flushUploads().

maxImageSize
number

Maximum file size in bytes.

acceptedImageTypes
string[]

Array of MIME types accepted for upload.

onUploadError
(message: string, file?: File) => void

Called when validation or upload fails.

sanitize
(html: string) => string

Custom sanitizer for paste and preview (defaults to built-in allow-list).

sanitizerConfig
HtmlSanitizerConfig

Override the allow-list used by the built-in sanitizer.

openLinksInNewTab
boolean"true"

Open preview links with target="_blank".

spellCheck
boolean"true"

Enable browser spellcheck on the editable surface.

autoFocus
boolean"false"

Focus the editor on mount.

ariaLabel
string"'Rich text editor'"

Accessible label for the editor.

HtmlPreview Props

value
string

HTML source to render.

sanitize
(html: string) => string

Custom sanitizer — defaults to the built-in allow-list.

sanitizerConfig
HtmlSanitizerConfig

Override the allow-list used by the built-in sanitizer.

openLinksInNewTab
boolean"true"

Add target="_blank" rel="noopener" to links.

emptyFallback
React.ReactNode

Shown when value is empty.

Features

True WYSIWYG

contentEditable-based rich text surface — you see formatting as you type.

Complete formatting

Bold, italic, underline, strike, code, sup/sub, highlight, colors, fonts, alignment, tables, lists.

Safe by default

Built-in HTML sanitizer strips scripts, event handlers, and dangerous URL schemes.

Image uploads

Immediate or deferred uploads with paste, drop, and URL input flows.

Configurable toolbar

Pick any subset of 30+ toolbar actions, or hide the toolbar entirely.

Theme-aware

All colors flow from --eui-* variables — auto-supports light, dark, and brand themes.