shadcn.io is not affiliated with official shadcn/ui
Markdown Editor Form Block for React
A page editor whose toolbar really formats: Bold, Italic, Bullet list and Link wrap the selection in Markdown, shortcuts work, the toolbar is one tab stop with arrow keys, and a Preview tab renders the result.
Installation
Overview
Format text without a rich-text engine, with this Markdown editor form for React with Base UI. A compact header sits over a hairline frame with a plus mark on each corner. A toolbar band holds Bold, Italic, Bullet list and Link buttons and the Write and Preview Tabs. Each button wraps or prefixes the selection in the Textarea with Markdown (**, _, - , [text](url)) and takes it off again when it is already there; Ctrl or Cmd + B and + I do the same. The toolbar is a role="toolbar" with a single tab stop and the arrow keys, Home and End to move along it, and its toggle buttons are aria-pressed while the caret or selection sits inside bold, italic or list text. Preview renders the Markdown as React elements (bold, italic, bullet lists and links, never dangerouslySetInnerHTML). Under the editor a word and character count; Publish page, the only Shine Button, says nothing was saved. It's built from Textarea, Tabs, Field and Guides. For wikis, notes, comments and any text field where formatting is Markdown.
FAQ
How does a toolbar button change the text?
It reads the textarea's value and selection from the page, runs a pure function (boldEdit, italicEdit, listEdit, linkEdit) that returns the new text and what to select, sets the state and puts the selection back in the field. Bold on a selection adds ** around it, or removes them when they are already there; with nothing selected it inserts a placeholder word and selects it so typing replaces it. Link wraps the selection as [text](https://) and selects the address. Because the text is Markdown, undo works with the browser's own history for typing, but not for toolbar edits: a real editor would keep its own stack.
How is the Preview rendered, and is it safe?
A small parser in the file splits the text into paragraphs and bullet lists, then each line into plain, bold, italic and link pieces, and renders them as elements, so no HTML from the text is ever inserted. Links only keep addresses that start with http, https, mailto or #; anything else becomes #, and in this demo a link does not navigate. It is a subset on purpose: headings, images, tables and code are left out. For the full language use a Markdown library and sanitise its output.
How does the keyboard work on the toolbar?
The toolbar is one tab stop (a roving tabIndex): Tab enters at the last button used, the arrow keys move along it (reversed on a right-to-left page), Home and End jump to the ends, and Enter or Space press a button. The toggle buttons expose aria-pressed, which follows the caret as it moves, and while Preview is showing they stay focusable but quiet and say so instead of acting. Ctrl or Cmd + B and + I work inside the textarea.
Why is there no contenteditable?
Contenteditable behaves differently in every browser and is hard to make accessible; a textarea has one selection model, native undo for typing, spell checking and a mobile keyboard that works. Showing the Markdown beside a rendered preview is honest about what is stored. If you need real rich text, pick an editor built for it and keep this block's toolbar pattern and its keyboard support.
What stays local, and how do I connect it for real?
The page text lives in the block's state and Publish page only says it was published in this demo: nothing is saved, sent or kept. In your app post the Markdown from the submit handler, render it on the server with the same subset (or a sanitising library), and show the server's answer in the status line.
How do I connect it to react-hook-form or TanStack Form?
Keep the Markdown string as the field's value. With react-hook-form, use Controller around the textarea and call field.onChange from the same handlers, including the toolbar's apply. With TanStack Form, validate the length on change and leave the formatting functions pure, so they can be tested without the form.
Related
- Pro
Textarea with Counter Ring Form
Text fieldsA stand-up update composer whose character counter is a ring that fills to 280, shows its number only near the end, goes negative past the limit and is spoken at four stages, with a list of the latest two posts.
- Pro
Minimal Single Question Form
Text fieldsThe calmest form: one page with a mark, a heading that asks one question, one field with a hidden label and one full-width button, with the answer or the one problem shown under the field.
- Pro
Search Input with Recent Searches Form
Text fieldsA catalogue search form with a clear button, a slash shortcut, Escape to clear, results marked as matches in a held-height region, an empty state with a way back and recent searches as removable chips.
- Pro
URL Input with Paste Cleanup Form
Text fieldsA website field with a fixed https:// in front that cleans what you paste, then reads the address with the browser's URL parser into Host, Path and Secure cells.
- Pro
Email and Password Form
Text fieldsThe sign-in pair done well: an email named for password managers, a password with a reveal button and a Caps Lock warning, one generic failure that never says which half was wrong, and a quiet band with the demo account.
- Pro
Password Strength Form
Text fieldsA master-password form with advice instead of a checklist: a four-segment meter, one sentence that names the weakest thing about what is typed, a polite confirm field and a side cell that explains the four segments.