Skip to main content
Light Dark System

Remarkd Editor

<zn-remarkd-editor> | ZnRemarkdEditor
Since 1.0 experimental

A Notion-style block editor for remarkd content. Blocks render inline; click one to edit its source.

zn-remarkd-editor is a Notion-style editor for remarkd content. The document is a list of blocks rendered inline with remarkd — there is no separate preview. Click a block to edit its raw remarkd source in place; it re-renders when you click away (or press Escape / Ctrl+Enter). The value is always plain remarkd source, submitted with the surrounding form.

<zn-remarkd-editor
  name="content"
  value="# Product Guide

This is a paragraph with **strong text**, __emphasis__, and a [link](https://example.com).

- [ ] Draft the guide
- [x] Review the output

NOTE: Click any block to edit its source."
></zn-remarkd-editor>

Examples

Adding and Moving Blocks

Hover a block to reveal its actions: a drag handle with an add-block button below it in the left gutter, and a delete button in its top-right corner. Clicking the empty area at the end of the document starts a new block. A block committed empty is removed.

<zn-remarkd-editor
  name="content"
  value="Hover me to see the block actions."
></zn-remarkd-editor>

Slash Menu

Typing / in an empty block opens a slash menu of block types at the caret. Keep typing to narrow it, / to move, Enter or Tab to insert, Escape to dismiss. The menu only appears in a block that is nothing but the slash command — a prefix like ## is not valid remarkd part-way through a line — so / in the middle of a sentence stays literal text.

Choosing a block type replaces the command with its remarkd prefix and leaves the caret ready to type: Heading 2 gives you ## , Code opens a fence with the caret inside it, and Image opens the upload picker instead of inserting text. The same block types are on the toolbar, which inserts them as new blocks at the end of the document.

<zn-remarkd-editor
  name="content"
  value="Click the empty space below this block, then type / to see the menu."
></zn-remarkd-editor>

Images

Adding an image shows a zn-file drop area inline, at the point in the document where the image will go. Choosing a file uploads it straight away: the file’s metadata is POSTed to the attachment-url endpoint, which must respond with {uploadPath, uploadUrl}; the file is then PUT to uploadUrl and the returned uploadPath is embedded as the image URL. Dropping an image file straight onto the editor uploads it directly. attachment-url is required for image support.

Clicking an image block opens its controls: caption, alignment (left / center / right), width, height, and alt text — plus edit-source and delete actions. These write standard remarkd syntax back to the value: the caption as a .Caption title line, alignment as an [.align-center] / [.align-right] class, and size as image::src[alt,width,height].

<zn-remarkd-editor
  name="content"
  value=".The Zinc logo
[.align-center]
image::/assets/images/watermark.svg[Zinc,220]"
></zn-remarkd-editor>

Remarkd Blocks

Remarkd’s block syntax — hints, containers, code fences — renders with the official remarkd styles. Fenced content stays a single block, blank lines and all.

<zn-remarkd-editor name="content" value="TIP: Hint blocks are remarkd-specific.

====
An example **container** block.
====

```
a code fence

with a blank line inside
```"
></zn-remarkd-editor>

Raw Source

Add allow-raw for a toolbar toggle that swaps the block view for the whole document as editable remarkd source. Useful for pasting in a finished document or fixing syntax the block view makes awkward. Toggling back re-splits the source into blocks, normalising the spacing between them to a single blank line. The toggle is only offered on editable editors — readonly and disabled instances hide the toolbar entirely.

<zn-remarkd-editor
  name="content"
  allow-raw
  value="# Raw Source

Hit the toggle at the right of the toolbar to edit this whole document at once.

NOTE: The value is the same remarkd source either way."
></zn-remarkd-editor>

Height

The editor never grows taller than the viewport: past that, its body scrolls and the toolbar stays in reach. Override the cap with --remarkd-editor-max-height — useful when the editor sits below a header and should only fill the space left on screen.

All scrolling stays inside the editor’s own body: adding a block or opening the image / include picker brings the new content into view, and dragging a block to a scroll edge keeps the content moving. Neither touches the page or panel around the editor.

<zn-remarkd-editor
  style="--remarkd-editor-max-height: 20rem"
  value="A short editor.

Add blocks from the toolbar and the body starts scrolling once the content
outgrows the 20rem cap set here."
></zn-remarkd-editor>

Actions

The toolbar and the / slash menu are driven by one shared registry of actions, grouped under ten headings: Text, Inline, Lists, Admonitions, Blocks, Structured, Media, Objects, Breaks, and Logic. The toolbar shows as many groups as fit its width and folds the rest into an overflow menu; the slash menu carries every action regardless of width, and typing narrows it by label or keyword — so a folded toolbar group is never actually out of reach.

<zn-remarkd-editor value='{{button:action text="Get started" href=/}}

{{video:dQw4w9WgXcQ source=youtube}}

{{reflist}}'></zn-remarkd-editor>

Inline Formatting

Inline actions — Strong, Emphasis, Code, Underline, Strikethrough, and the rest of the Inline group — apply to the current selection inside the block being edited, so they only make sense while a block is open: the toolbar disables them until one is (the slash menu lists them regardless, since it can only ever open inside a block in the first place). Choosing one from the toolbar wraps the selection in its mark, or — with nothing selected — inserts a placeholder inside the mark with the placeholder selected so typing replaces it; from the slash menu it always inserts the full construct with the caret collapsed at the start of the placeholder, since there is no selection to wrap. Choosing the same action again on a selection that already carries the mark unwraps it rather than nesting a second one.

<zn-remarkd-editor value="Click into this block, select a word, then choose Strong or Emphasis from the toolbar."></zn-remarkd-editor>

Variables and Conditionals

The Logic group’s actions insert document attributes (:name: value) and remarkd’s conditional directives (ifdef, ifndef, ifeval) — but the editor only ever marks them, it never evaluates them: an attribute line renders as a chip, a {name} reference in surrounding text renders as a token, and an ifdef/ifndef/ifeval range renders as a labelled wrapper whose content stays visible regardless of whether the flag is defined. Evaluating conditionals belongs to the renderer the finished document is published through, not to this editor — showing both branches of an if/else here means neither one ever disappears while you’re still editing it.

<zn-remarkd-editor value=":product: Remarkd

This documents {product}.

ifdef::beta[]
Only for the beta.
endif::[]"></zn-remarkd-editor>

Form Integration

zn-remarkd-editor is a form control; its remarkd source is submitted under name, and required is supported.


Submit
<form class="remarkd-editor-form">
  <zn-remarkd-editor name="content" required value="Edit me, then submit."></zn-remarkd-editor>
  <br />
  <zn-button type="submit" color="success">Submit</zn-button>
</form>

<script type="module">
  const form = document.querySelector('.remarkd-editor-form');

  await customElements.whenDefined('zn-button');
  await customElements.whenDefined('zn-remarkd-editor');

  form.addEventListener('submit', (e) => {
    e.preventDefault();
    const data = Object.fromEntries(new FormData(form));
    alert('Submitted!\n\n' + JSON.stringify(data, null, 2));
  });
</script>

Importing

If you’re using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use any of the following snippets to cherry pick this component.

To import this component from the CDN using a script tag:

<script type="module" src="https://cdn.jsdelivr.net/npm/@kubex/zinc@1.1.132/dist/components/remarkd-editor/remarkd-editor.js"></script>

To import this component from the CDN using a JavaScript import:

import 'https://cdn.jsdelivr.net/npm/@kubex/zinc@1.1.132/dist/components/remarkd-editor/remarkd-editor.js';

To import this component using a bundler:

import '@kubex/zinc/dist/components/remarkd-editor/remarkd-editor.js';

Properties

Name Description Reflects Type Default
slashRecentKey
slash-recent-key
Lists the blocks most recently inserted here above the rest of the slash menu, remembered in localStorage under this key. Leave unset to offer no recently used section. string ''
name The name of the control, submitted as part of form data. string ''
value The current remarkd source. string ''
defaultValue The default value — used when resetting the form. string ''
placeholder Placeholder shown when the document is empty. string 'Type something…'
attachmentUrl
attachment-url
Endpoint for image uploads — required for image support. Posting the file metadata here must return {uploadPath, uploadUrl}; the file is then PUT to uploadUrl and the returned uploadPath is embedded as the image URL. string ''
includeUrl
include-url
Endpoint listing the Includes this document may embed, as {"items":[{id,title,description,scope,keywords,languages,url}]}. Labels the chips rendered for include:: directives and feeds the include picker. string ''
linkUrl
link-url
Endpoint the article link picker searches, as {"items":[{ref,kind,title,context,status}]}. Queried with ?q=<term> as the author types and with ?refs=a,b to resolve the references a body already carries. string ''
allowRaw
allow-raw
Adds a toolbar toggle that swaps the block view for the full remarkd source. boolean false
required Makes the editor required for form submission. boolean false
readonly Makes the editor read-only. boolean false
disabled Disables the editor. boolean false
updateComplete A read-only promise that resolves when the component has finished updating.

Learn more about attributes and properties.

Events

Name React Event Description Event Detail
zn-input Emitted on each keystroke while editing a block. -
zn-change Emitted when a block edit is committed and the value changes. -

Learn more about events.

Methods

Name Description Arguments
focus() Starts editing the first block, or a new block if the document is empty. -
blur() Commits any in-progress block or raw edit. -

Learn more about methods.

Custom Properties

Name Description Default
--remarkd-editor-max-height The tallest the editor grows before its body scrolls. Defaults to 100dvh.

Learn more about customizing CSS custom properties.

Parts

Name Description
base The component’s base wrapper.
toolbar The always-visible block-insert and inline-formatting toolbar.
raw-toggle The button that switches between the block view and the raw source view.
block A rendered block wrapper.
rendered The rendered remarkd output of a block.
conditional A labelled wrapper for an ifdef/ifndef/ifeval/iftrue/iffalse/ifempty/ifnempty range.
variable A chip rendered for a document attribute, title, or bracket line.
input The textarea shown while editing a block.
raw The full-document textarea shown in raw source mode.
slash-menu The zn-slash-menu opened by typing ”/” in an empty block.
image-controls The caption / alignment / size panel shown when an image block is clicked.
include The chip rendered in place of an include:: directive.
include-picker The inline Include picker opened from the toolbar or ”/include”.

Learn more about customizing CSS parts.

Dependencies

This component automatically imports the following dependencies.

  • <zn-button>
  • <zn-button-group>
  • <zn-dialog>
  • <zn-dropdown>
  • <zn-example>
  • <zn-file>
  • <zn-icon>
  • <zn-menu>
  • <zn-menu-item>
  • <zn-slash-menu>
  • <zn-tooltip>