wiremd is a text-first UI design tool that converts Markdown with extended wireframing syntax into visual wireframes. Write your UI in plain text; render it as HTML with various visual styles, as a React component, as Tailwind-classed HTML, or as JSON.
wiremd is built on standard CommonMark Markdown (via unified/remark) with custom extensions for UI components. All standard Markdown syntax works as expected:
| Syntax | Example |
|---|---|
| Headings | # H1, ## H2, ### H3 … ###### H6 |
| Bold | **bold** |
| Italic | *italic* |
| Code (inline) | `code` |
| Link | [text](url) |
| Image |  |
| Unordered list | - item |
| Ordered list | 1. item |
| Blockquote | > quoted text |
| Table | | col | col | with --- separator row |
| Horizontal rule | --- |
| Fenced code block | ``` … ``` |
The extensions add three things on top: ::: container blocks for layout and component grouping, [[ ]] inline containers for navigation bars, and inline button/input shorthand such as [Label]* and [_____].
wiremd requires Node.js. Install it as a library (npm install wiremd) for programmatic use, or install it globally (npm install -g wiremd) to use the wiremd CLI command. There is no web-based playground at this time.
See the Components reference for all syntax and components.
A button uses square brackets with no URL: [Label]. A standard Markdown link uses square brackets followed by a URL in parentheses: [Label](https://example.com). wiremd sees any bare [Label] (or [Label]* for primary, [Label]{variant:danger} for variants) as a button node, and any [Label](url) as a link node.
A line of plain text immediately above an input line — with no blank line between them — is treated as that input's label. A blank line breaks the association and the text is rendered as a paragraph instead.
![[path.md]] is the file-include syntax. When wiremd parses a file it first runs resolveIncludes, which finds every ![[relative/path.md]] pattern outside of fenced code blocks and replaces it with the contents of the referenced file. This lets you split large wireframes into reusable partials. Includes inside code fences are left untouched.
No. The {key:value} and {.class} attribute syntax is a wiremd extension. Attributes must appear immediately after the element they modify (one space is acceptable). They apply to the directly preceding element — a button, input, heading, or container.
All standard HTML5 input types can be specified via the type attribute:
[_____________________________]{type:text}
[_____________________________]{type:email}
[_____________________________]{type:password}
[_____________________________]{type:tel}
[_____________________________]{type:url}
[_____________________________]{type:number}
[_____________________________]{type:date}
[_____________________________]{type:search}
[_____________________________]{type:file}Checkboxes use standard Markdown task list syntax — - [x] for checked, - [ ] for unchecked. Radio buttons use a parenthesis variant — - (*) for selected, - ( ) for unselected. wiremd maps these to the appropriate input types in the rendered HTML.
Yes. Use ::: tabs with ::: tab Label children:
::: tabs
::: tab Overview
Content for first tab.
:::
::: tab Settings
Content for second tab.
:::
:::Accordion syntax (::: accordion) parses correctly but is not yet rendered — the renderer has no implementation for it. It falls back to unstyled output. See the Not Implemented page for the planned syntax.
Three: primary, secondary, and danger. Use the asterisk shorthand for primary ([Submit]*), or the variant key-value attribute for any variant:
[Submit]* # Primary — preferred shorthand
[Submit]{variant:primary} # Primary — explicit form
[Cancel]{variant:secondary} # Secondary
[Delete]{variant:danger} # Danger/destructiveNote: {.primary}, {.secondary}, and {.danger} (dot-prefix class syntax) add raw CSS classes, not variant classes. They have no built-in style definitions and will not produce styled buttons. Use * or {variant:name} instead.
Yes, using the state attribute:
[Submit]{state:disabled}
[Processing...]{state:loading}
[Saved]{state:success}
[Error]{state:error}Note: {disabled} (boolean syntax without state:) works on inputs, textareas, and selects, but is silently ignored on buttons. Always use {state:disabled} for buttons.
grid-2 through grid-5 (2, 3, 4, or 5 equal columns). Add card to get card chrome on each cell. On small viewports the columns collapse to a single column via CSS media queries.
Yes. The ::: container syntax supports arbitrary nesting. The remark-containers plugin tracks depth so an inner ::: closes only its own block, not the outer one.
Seven styles, passed with --style:
| Style | Description |
|---|---|
sketch |
Balsamiq-inspired hand-drawn look (default) |
clean |
Modern minimal design |
wireframe |
Traditional grayscale with hatching |
material |
Google Material Design with elevation |
brutal |
Neo-brutalist with bold colors and thick borders |
tailwind |
Utility-first design with purple accents |
none |
Unstyled semantic HTML |
The CLI supports --format html (default) and --format json. The Node.js API additionally exposes renderToReact() for JSX/TSX output and renderToTailwind() for Tailwind-classed HTML — these are not available as CLI flags and must be called programmatically.
Use the {.classname} attribute syntax on any element. Multiple classes can be space-separated within the same braces:
[Button]{.btn .btn-lg .custom}
::: card {.highlight-card}
Content
:::Classes are passed through to the rendered HTML element unchanged.
No. wiremd generates static HTML (or JSX/JSON). Components such as tabs, accordions, and modals produce the correct semantic markup and ARIA-friendly structure, but client-side behaviour (switching tabs, expanding accordion items) must be provided by your own CSS and JavaScript, or by the framework you use with the React output.
See the Components reference for all syntax and components, and JSON Schema for the JSON output schema.
Search existing issues first at github.com/teezeit/wiremd/issues. If your issue is not there, open a new one with a minimal reproduction (the smallest wiremd source that demonstrates the problem), the expected output, and the actual output. For feature requests, include a description, your use case, and an example of what the syntax might look like.
Install globally or use npx:
npm install -g wiremd # then: wiremd file.md
# or without installing:
npx wiremd file.md--serve requires an explicit port. If it's taken, pick another:
wiremd file.md --serve 3001Check for extra spaces or missing brackets:
[Submit]* ✓
[ Submit ] ✗ extra spaces
[Submit ✗ missing closing bracketThe input must have enough underscores (at least 3) and no blank line between the label and the field:
Username ✓ label directly above
[_____________________________]
Username ✗ blank line breaks association
[_____________________________]Each column needs a ### heading inside the container:
::: grid-3 card
### Column 1
Content
### Column 2
Content
:::