> ## Documentation Index
> Fetch the complete documentation index at: https://docs.revoengine.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Preview a Markdown draft

> Build a read-only Markdown reply form and refresh its committed preview.

This complete Inline JavaScript widget lets an operator edit Markdown and inspect a committed preview. **Prepare preview** commits the draft and refreshes only the preview cell. It does not send or queue a message.

## Prerequisites

Create a Custom Widget, select **Inline / JavaScript**, and keep view execution read-only. No target registry is needed: the form uses REFRESH in its own frame. Leave Inputs as `{}` to try the example without uploads. If uploads are needed, configure `uploadFolderId` with an existing folder the viewer may write to.

## Source

```js theme={null}
const input = api.input() ?? {};
const ui = input.$widget ?? {};
const targets = ui.targetIds ?? ['*'];
const wanted = (id) => targets.includes('*') || targets.includes(id);
const empty = {format: 'markdown', body: '', attachments: []};
const draft = ui.values?.reply ?? empty;
if (!draft || draft.format !== 'markdown' || typeof draft.body !== 'string') {
  throw new Error('Expected a Markdown message draft');
}
const rows = [];
if (wanted('replyForm')) {
  rows.push([{
    id: 'replyForm', type: 'FORM', label: 'Public reply',
    submitLabel: 'Prepare preview',
    fields: [{
      id: 'reply', type: 'INPUT', control: 'message', label: 'Message',
      value: empty, valueMode: 'initial', height: 240, maxLength: 12000,
      ...(typeof input.uploadFolderId === 'string'
        ? {composer: {uploadFolderId: input.uploadFolderId}}
        : {})
    }],
    on: {submit: {type: 'REFRESH', target: ['markdownPreview']}}
  }]);
}
if (wanted('markdownPreview')) {
  rows.push([{
    id: 'markdownPreview', type: 'TEXT', label: 'Committed message', height: 360,
    presentation: {kind: 'markdown', copy: true, fullscreen: true},
    value: draft.body
  }]);
}
return rows;
```

## Expected result

1. Enter `Hello **customer**.` in the message field.
2. Select **Prepare preview**. The Markdown reader shows the committed message with **customer** in bold.
3. Edit the draft again. The prepared result stays unchanged until the next successful submit.
4. Refresh the widget. The stable `reply` control retains its draft.

The Markdown reader previews message content. Final email HTML/text and templates belong in your application library; this example does not render a finished email.

When an upload folder is configured, file selection, drag/drop or pasting a screenshot uploads a normal Storage attachment. Submission waits until uploads are ready or removed. The preview does not load or send those files; the committed draft retains their identities for a separately authorized writer.

## Failure behavior

An invalid draft, excessive text, an inaccessible upload folder or unfinished attachment leaves the preparation incomplete. Review the local error, remove or retry the attachment and submit again. Neither a successful upload nor a preview confirms delivery.

For a later sending workflow, register an explicit write action, validate recipients and current Storage access/version on the server, keep internal notes separate, and use its durable delivery/idempotency contract. See [message composer](https://docs.revoengine.com/widgets/message-composer.md), [execution](https://docs.revoengine.com/widgets/execute.md) and [state](https://docs.revoengine.com/widgets/state.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.