How to Extend Markdown with Custom Syntax Using Marked.js

This article explains how to create personalized Markdown syntax using the Marked library, covering basic usage, the internal six‑step rendering process, lexical analysis with regex, and step‑by‑step implementation of custom block elements like an alcohol card and video embed.

BaiPing Technology
BaiPing Technology
BaiPing Technology
How to Extend Markdown with Custom Syntax Using Marked.js

Why Create a Personalized Markdown Syntax?

Before discussing this topic we introduce our product "BaiPing", a professional whisky community where articles are stored in Markdown. When product managers saw other editors supporting rich media, they asked us to add similar capabilities, which led us to explore extending Markdown.

Simple Introduction to Marked Usage

We show a simple Markdown snippet and install Marked via npm, then convert it to HTML with a few lines of code.

# Hello Marked

My name is Juner.

Installation command: npm install marked Conversion code:

import marked from 'marked';
console.log(marked(the_content_above));

Deep Dive into Marked

Marked’s rendering process consists of six steps:

Receive the user’s Markdown input;

The lexer analyzes the input and builds a nested token tree;

Each token is produced by matching a regular expression, capturing the matched string as the token’s content;

Before exporting the final token tree, a pass applies custom processing such as lowering heading levels;

The renderer receives the token tree, traverses it, and assigns rendering functions to each token;

Each rendering function receives a token and generates the corresponding HTML fragment.

We illustrate the process with a flowchart:

Lexical Analysis Process

Marked’s lexer uses regular expressions to match block‑level and inline syntax. For example, a paragraph token is created by matching text until a blank line, and the lexer recursively processes the remaining source.

function lex(src) {
  const tokens = [];
  // ... lexical analysis logic ...
  return tokens;
}

Regex Matching Principle

The blockquote rule matches lines starting with ">" after applying the paragraph regex. The heading rule matches lines beginning with one or more "#" characters.

Extending Marked with Custom Syntax

Marked follows the single‑responsibility and open‑closed principles, allowing extensions via marked.use({...}). We define two custom block syntaxes: an alcohol card and a video embed.

Alcohol card syntax starts with @alcohol followed by a JSON object; video syntax starts with v[] and a URL to an MP4 file.

const alcoholPattern = /^@alcohol (.+)(?:
|$)/;
const videoPattern = /^v\[]\((https?:\/\/.+\.mp4)\)(?:
|$)/;

Tokenizer Functions

Each tokenizer returns an object containing at least type and raw. The alcohol tokenizer also parses the JSON and stores it in data.

function alcoholTokenizer(src) {
  const match = alcoholPattern.exec(src);
  if (match) {
    const json = match[1].trim();
    try {
      return {
        type: 'alcohol',
        raw: match[0],
        text: json,
        data: JSON.parse(json)
      };
    } catch (err) {
      console.error(err);
    }
  }
  return null;
}
function videoTokenizer(src) {
  const match = videoPattern.exec(src);
  if (match) {
    return {
      type: 'video',
      raw: match[0],
      text: match[1],
      link: match[1]
    };
  }
  return null;
}

Renderer Functions

The alcohol renderer outputs a section with an image, name, and score; the video renderer outputs a <video> element with a source.

function alcoholRenderer(token) {
  const { id, name, cover, score } = token.data;
  return `
    <section id="alcohol-${id}">
      <img src="${cover}" alt="">
      <div class="message">
        <p class="name">${name}</p>
        <p class="score">${score}</p>
      </div>
    </section>`;
}
function videoRenderer(token) {
  return `
    <video controls>
      <source src="${token.link}" type="video/mp4">
      <p>本视频暂不支持播放</p>
    </video>`;
}

Complete Code

We register the extensions and parse a sample Markdown string containing both custom syntaxes.

import marked from 'marked';

const alcoholRule = {
  name: 'alcohol',
  level: 'block',
  start(src) { return src.match(/@alcohol {[^
]*}/)?.index; },
  tokenizer: alcoholTokenizer,
  renderer: alcoholRenderer
};

const videoRule = {
  name: 'video',
  level: 'block',
  start(src) { return src.match(/v\[]\(https?:\/\//)?.index; },
  tokenizer: videoTokenizer,
  renderer: videoRenderer
};

marked.use({ extensions: [alcoholRule, videoRule] });

const markdown = `@alcohol {"id":12345,"name":"罗曼湖","cover":"http://example.com/example.jpg","score":10}

v[](http://example.com/example.mp4)`;
console.log(marked.parse(markdown));

The resulting HTML renders the alcohol card and the video player.

<section id="alcohol-12345">
  <img src="http://example.com/example.jpg" alt="">
  <div class="message">
    <p class="name">罗曼湖</p>
    <p class="score">10</p>
  </div>
</section>
<video controls>
  <source src="http://example.com/example.mp4" type="video/mp4">
  <p>本视频暂不支持播放</p>
</video>

Conclusion

We have satisfied the product requirement with simple custom syntaxes. More complex elements such as tables, mathematical expressions, or flowcharts would require advanced regular expressions and deeper knowledge of Marked’s internals.

For further exploration, refer to the Extensibility section of the Marked documentation.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

markdownParserrendererCustom SyntaxMarked
BaiPing Technology
Written by

BaiPing Technology

Official account of the BaiPing app technology team. Dedicated to enhancing human productivity through technology. | DRINK FOR FUN!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.