Fluxo UIFluxo UIv0.4.1

Markdown Editor & Preview

A zero-dependency markdown editor and renderer with toolbar configuration, image upload callbacks, split view, and full dark-mode and theme support.

Basic Usage

Editor with Live Preview

Edit and preview markdown with view toggling, keyboard shortcuts, and a complete toolbar.

Markdown Editor Showcase

A zero-dependency editor with full inline and block formatting. Inline marks: bold, italic, *bold italic*, strikethrough, and inline code.

Links and images

Visit FluxoUI on utilsware — or check an autolink: https://utilsware.com.

Mountain landscape

Lists

Unordered with nesting:

  • First item

  • Second item

    • Nested one
    • Nested two
      • Even deeper
  • Third item

    Ordered list:

    1. Wire up the toolbar

    2. Paste an image

    3. Flush uploads on submit

      Task list:

  • Parser supports GFM

  • Split view with mobile tabs

  • Syntax highlighting (coming soon)

Blockquote

"Markdown should be easy to write — and even easier to read."

— every documentation author ever

Fenced code

import { MarkdownEditor } from 'fluxo-ui';

const editor = <MarkdownEditor value={md} onChange={setMd} />;

Table

FeatureInlineBlock
Headingsyes
Imagesyesyes
Tablesyes
Task listsyes

Try editing any of the above, or click the image, link, or table buttons in the toolbar.

205 words · 1324 chars
import { MarkdownEditor } from 'fluxo-ui';

const [value, setValue] = useState('# Hello');

<MarkdownEditor
  value={value}
  onChange={setValue}
  defaultView="split"
  minHeight={420}
/>

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.

Forest

  • Edit-only hides the preview pane

  • Split shows both side-by-side (tabs on mobile)

  • Preview hides the editor entirely

    1. Try the Edit button

    2. Try Split

    3. Try Preview

      Each view can be controlled externally via the view prop.

<MarkdownEditor view={view} onViewChange={setView} />
ModeEditorPreview
edityesno
splityesyes
previewnoyes
109 words · 763 chars
Current view: split
const [view, setView] = useState<EditorViewMode>('split');

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

Preview Only

Preview Only

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

Preview-only Renderer

Use MarkdownPreview when you only need to render markdown — comments, blog posts, or read-only docs.

What this renderer supports

Inline: bold, italic, *bold italic*, strike, code, and links.

Ocean sunset

Lists with nesting

  1. First top-level item

  2. Second with a nested list

    • Nested bullet
    • Another nested bullet
      1. Deep ordered
      2. Deep ordered two
  3. Third top-level item

Task list

  • Safe URL sanitization

  • Lazy-loaded images

  • Custom image resolver

Blockquote

Blockquotes can span multiple lines and contain inline formatting.

They can even contain code and links.

Fenced code block

import { MarkdownPreview } from 'fluxo-ui';

<MarkdownPreview value={markdown} openLinksInNewTab />

Table with alignment

Left-alignedCenteredRight-aligned
onetwothree
shortlongtext
abc

Unsafe URLs like javascript:alert(1) are automatically blocked.

import { MarkdownPreview } from 'fluxo-ui';

<MarkdownPreview value={markdown} openLinksInNewTab />

Custom Toolbar

Minimal Toolbar

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

72 words · 560 chars

Custom Selection

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

72 words · 560 chars

No Toolbar

Disable the toolbar entirely — keyboard shortcuts still work.

72 words · 560 chars
import { MarkdownEditor, MINIMAL_MARKDOWN_TOOLBAR } from 'fluxo-ui';

<MarkdownEditor toolbar={MINIMAL_MARKDOWN_TOOLBAR} />

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

<MarkdownEditor toolbar={false} />

Image Upload — Immediate

Immediate Upload Strategy

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

118 words · 757 chars
<MarkdownEditor
  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.

121 words · 815 chars
const editorRef = useRef<MarkdownEditorHandle>(null);

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

<Button
  onClick={async () => {
    const finalMarkdown = await editorRef.current?.flushUploads();
    submitForm(finalMarkdown);
  }}
>
  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 — the textarea remains selectable, but the toolbar is disabled.

What you can see

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

A calm lake

  • Useful for displaying the editor with its chrome intact

  • Prevents any formatting changes

  • Still allows copy to clipboard

    1. Open this in split view

    2. Try clicking a toolbar button — it's disabled

    3. Text remains selectable so users can copy it

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

<MarkdownEditor value={markdown} readOnly defaultView="split" />
BehaviorRead-only
Toolbar enabledno
Text selectableyes
Shortcuts workno
Preview updatesyes
136 words · 960 chars
<MarkdownEditor value={value} readOnly defaultView="split" />

Import

import { MarkdownEditor, MarkdownPreview } from 'fluxo-ui';
import type { MarkdownEditorProps, MarkdownEditorHandle, MarkdownPreviewProps } from 'fluxo-ui';

MarkdownEditor Props

value
string

Controlled markdown value.

defaultValue
string

Initial value when uncontrolled.

onChange
(value: string) => void

Called whenever the markdown changes.

placeholder
string"'Write markdown...'"

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
MarkdownToolbarItem[] | false"DEFAULT_MARKDOWN_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 on selection 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.

openLinksInNewTab
boolean"true"

Open preview links with target="_blank".

spellCheck
boolean"true"

Enable browser spellcheck on the textarea.

autoFocus
boolean"false"

Focus the editor on mount.

ariaLabel
string"'Markdown editor'"

Accessible label for the textarea.

MarkdownPreview Props

value
string

Markdown source to render.

openLinksInNewTab
boolean"true"

Render links with target="_blank" rel="noopener".

sanitizeUrl
(url: string) => string | null

Custom URL sanitizer — return null to block.

imageResolver
(src: string) => string

Rewrite image URLs before rendering.

emptyFallback
React.ReactNode

Shown when value is empty.

Features

Zero Dependencies

Custom markdown parser and renderer with no third-party libraries.

Image Uploads

Plug in any async upload callback. Immediate or deferred (flush on submit) strategies.

Toggle Views

Edit-only, preview-only, or split — with mobile tabs and keyboard shortcuts.

Accessible

Full keyboard operability, ARIA roles, focus trap in dialogs, high-contrast themes.

Configurable Toolbar

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

Theme-aware

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