1. Home
  2. Docs
  3. docs
  4. Widgets
  5. Markdown

Markdown

The markdown widget provides a full fledged text editor allowing users to format text with features such as headings and blockquotes. Users can change their editing view with a handy toggle button.

Please note: If you want to use your markdown editor to fill a markdown file contents after its frontmatter, you will have to name the field body so Static CMS recognizes it and saves the file accordingly.

Widget Options

For common options, see Common widget options.

Name

Type

Default

Description

default

string

''

Optional. The default value for the field. Accepts markdown content

media_folder

string

Optional. Specifies the folder path where uploaded files should be saved, relative to the base of the repo

public_folder

string

Optional. Specifies the folder path where the files uploaded by the media library will be accessed, relative to the base of the built site

media_library

Media Library Options

{}

Optional. Media library settings to apply when the media library is opened by the current widget. See Media Library

choose_url

boolean

true

Optional. When set to false, the “Insert from URL” button will be hidden

toolbar_buttons

object

[]

Optional. Specifies which toolbar items to show for the markdown widget. See Toolbar Customization

show_raw

boolean

false

Optional. Specifies if the raw markdown editor should be shown

Example

name: body
label: Body
widget: markdown
name: 'body',
label: 'Body',
widget: 'markdown',

This would render as:

Markdown widget example

Please note: The markdown widget outputs a raw markdown string. Your static site generator may or may not render the markdown to HTML automatically. Consult with your static site generator’s documentation for more information about rendering markdown.

Toolbar Customization

Name

Type

Default

Description

main

object

See Main Toolbar

Optional. A list of buttons and menus for the top level toolbar

empty

object

See Empty Toolbar

Optional. A list of buttons and menus for the popup toolbar when in an empty paragraph

selection

object

See Selection Toolbar

Optional. A list of buttons and menus for the popup toolbar when selecting text outside of a table cell

table_empty

object

See Empty Table Cell Toolbar

Optional. A list of buttons and menus for the popup toolbar when in an empty table cell

table_selection

object

See Table Selection Toolbar

Optional. A list of buttons and menus for the popup toolbar when selecting text in a table cell

Options

All toolbars can be customized with a list consisting of buttons and dropdown menus.

Buttons

Buttons can be configured simply with their name. The following options are available:

  • blockquote
  • bold
  • code
  • code-block
  • decrease-indent
  • delete-column
  • delete-row
  • delete-table
  • file-link
  • font
  • image
  • increase-indent
  • insert-column
  • insert-row
  • insert-table
  • italic
  • ordered-list
  • shortcode
  • strikethrough
  • unordered-list

The following options are available for menus:

Name

Type

Description

label

string

The name and tooltip label for the menu

icon

string

Optional. The icon to use for the menu. If not supplied a default add icon will be used

groups

list of groups

A list groups of menu items. Each group is separated by a divider

The following options are available for menu groups:

Name

Type

Description

items

list of strings

The name of the toolbar buttons in the group. All buttons are available in menus except font and shortcode

Default Values

Below are the default values for the various toolbars:

Main Toolbar

main:
  - bold
  - italic
  - strikethrough
  - code
  - font
  - unordered-list
  - ordered-list
  - decrease-indent
  - increase-indent
  - shortcode
  - label: Insert
    groups:
      - items:
          - blockquote
          - code-block
      - items:
          - insert-table
      - items:
          - image
          - file-link
main: [
  'bold',
  'italic',
  'strikethrough',
  'code',
  'font',
  'unordered-list',
  'ordered-list',
  'decrease-indent',
  'increase-indent',
  'shortcode',
  {
    label: 'Insert',
    groups: [
      {
        items: ['blockquote', 'code-block'],
      },
      {
        items: ['insert-table'],
      },
      {
        items: ['image', 'file-link'],
      },
    ],
  },
];

Empty Toolbar

empty: []
empty: [];

Selection Toolbar

selection:
  - bold
  - italic
  - strikethrough
  - code
  - font
  - file-link
selection: ['bold', 'italic', 'strikethrough', 'code', 'font', 'file-link'];

Empty Table Cell Toolbar

table_empty:
  - bold
  - italic
  - strikethrough
  - code
  - insert-row
  - delete-row
  - insert-column
  - delete-column
  - delete-table
  - file-link
  - image
  - shortcode
table_empty: [
  'bold',
  'italic',
  'strikethrough',
  'code',
  'insert-row',
  'delete-row',
  'insert-column',
  'delete-column',
  'delete-table',
  'file-link',
  'image',
  'shortcode',
];

Table Selection Toolbar

table_selection:
  - bold
  - italic
  - strikethrough
  - code
  - insert-row
  - delete-row
  - insert-column
  - delete-column
  - delete-table
  - file-link
table_selection: [
  'bold',
  'italic',
  'strikethrough',
  'code',
  'insert-row',
  'delete-row',
  'insert-column',
  'delete-column',
  'delete-table',
  'file-link',
];

Shortcodes

Shortcodes can be added to customize the Markdown editor via registerShortcode.

Usage

[youtube|p6h-rYSVX90]

CMS.registerShortcode('youtube', {
  label: 'YouTube',
  openTag: '[',
  closeTag: ']',
  separator: '|',
  toProps: args => {
    if (args.length > 0) {
      return { src: args[0] };
    }

    return { src: '' };
  },
  toArgs: ({ src }) => {
    return [src];
  },
  control: ({ src, onChange }) => {
    return h('span', {}, [
      h('input', {
        key: 'control-input',
        value: src,
        onChange: event => {
          onChange({ src: event.target.value });
        },
      }),
      h(
        'iframe',
        {
          key: 'control-preview',
          width: '420',
          height: '315',
          src: `https://www.youtube.com/embed/${src}`,
        },
        '',
      ),
    ]);
  },
  preview: ({ src }) => {
    return h(
      'span',
      {},
      h(
        'iframe',
        {
          width: '420',
          height: '315',
          src: `https://www.youtube.com/embed/${src}`,
        },
        '',
      ),
    );
  },
});
import CMS from '@staticcms/core';

CMS.registerShortcode('youtube', {
  label: 'YouTube',
  openTag: '[',
  closeTag: ']',
  separator: '|',
  toProps: args => {
    if (args.length > 0) {
      return { src: args[0] };
    }

    return { src: '' };
  },
  toArgs: ({ src }) => {
    return [src];
  },
  control: ({ src, onChange }) => {
    return (
      <span>
        <input
          key="control-input"
          value={src}
          onChange={event => {
            onChange({ src: event.target.value });
          }}
        />
        <iframe
          key="control-preview"
          width="420"
          height="315"
          src={`https://web.archive.org/web/20240224001642/https://www.youtube.com/embed/${src}`}
        />
      </span>
    );
  },
  preview: ({ src }) => {
    return (
      <span>
        <iframe width="420" height="315" src={`https://web.archive.org/web/20240224001642/https://www.youtube.com/embed/${src}`} />
      </span>
    );
  },
});
import CMS from '@staticcms/core';

interface YouTubeShortcodeProps {
  src: string;
}

CMS.registerShortcode<YouTubeShortcodeProps>('youtube', {
  label: 'YouTube',
  openTag: '[',
  closeTag: ']',
  separator: '|',
  toProps: args => {
    if (args.length > 0) {
      return { src: args[0] };
    }

    return { src: '' };
  },
  toArgs: ({ src }) => {
    return [src];
  },
  control: ({ src, onChange }) => {
    return (
      <span>
        <input
          key="control-input"
          value={src}
          onChange={event => {
            onChange({ src: event.target.value });
          }}
        />
        <iframe
          key="control-preview"
          width="420"
          height="315"
          src={`https://web.archive.org/web/20240224001642/https://www.youtube.com/embed/${src}`}
        />
      </span>
    );
  },
  preview: ({ src }) => {
    return (
      <span>
        <iframe width="420" height="315" src={`https://web.archive.org/web/20240224001642/https://www.youtube.com/embed/${src}`} />
      </span>
    );
  },
});

Interacting With The Media Library

If you want to use the media library in your shortcode you will need to use the useMediaInsert and useMediaAsset hooks.

  • useMediaInsert – Takes the current url to your media, details about your field and a callback method for when new media is uploaded. If you want to select folders instead of files, set the forFoldervariable in options.
  • useMediaAsset – Transforms your stored url into a usable url for displaying as a preview.
const ImageControl = ({ src, onChange, controlProps }) => {
  const { collection, field, entry } = controlProps;

  const handleChange = ({ path }) => {
    onChange({ src: path });
  };

  const handleOpenMediaLibrary = useMediaInsert(
    src,
    { collection: collection, field },
    handleChange,
  );

  const assetSource = useMediaAsset(src, collection, field, entry);

  return [
    h('button', { type: 'button', onClick: handleOpenMediaLibrary }, 'Upload'),
    h('img', { role: 'presentation', src: assetSource }),
  ];
};
import useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset';
import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert';

const ImageControl = ({ src, onChange, controlProps }) => {
  const { collection, field, entry } = controlProps;

  const handleChange = ({ path }) => {
    onChange({ src: path });
  };

  const handleOpenMediaLibrary = useMediaInsert(
    src,
    { collection: collection, field },
    handleChange,
  );

  const assetSource = useMediaAsset(src, collection, field, entry);

  return (
    <>
      <button type="button" onClick={handleOpenMediaLibrary}>
        Upload
      </button>
      <img role="presentation" src={assetSource} />
    </>
  );
};
import useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset';
import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert';

import type { WidgetControlProps, MediaPath } from '@staticcms/core/interface';
import type { FC } from 'react';

const FileControl: FC<WidgetControlProps<string, MyField>> = ({ src, onChange, controlProps }) => {
  const { collection, field, entry } = controlProps;

  const handleChange = ({ path }: MediaPath) => {
    onChange({ src: path });
  };

  const handleOpenMediaLibrary = useMediaInsert(src, { collection, field }, handleChange);

  const assetSource = useMediaAsset(value, collection, field, entry);

  return (
    <>
      <button type="button" onClick={handleOpenMediaLibrary}>
        Upload
      </button>
      <img role="presentation" src={assetSource} />
    </>
  );
};