or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/marked@17.0.x

docs

index.md
tile.json

tessl/npm-marked

tessl install tessl/npm-marked@17.0.0

A markdown parser built for speed

options.mddocs/reference/

Configuration Options

Configuration options for customizing marked's parsing and rendering behavior.

MarkedOptions Interface

interface MarkedOptions {
  /**
   * Enable async parsing - returns Promise from parse functions
   * Required when using async walkTokens or async hooks
   * Default: false
   */
  async?: boolean;

  /**
   * Enable GFM line breaks (requires gfm: true)
   * Converts single \n in paragraphs to <br>
   * Without this, only two spaces + \n creates <br>
   * Default: false
   */
  breaks?: boolean;

  /**
   * Enable GitHub Flavored Markdown features:
   * - Tables (pipe-delimited)
   * - Strikethrough (~~text~~)
   * - Autolinks (raw URLs become clickable)
   * - Task lists (- [ ] and - [x])
   * Default: true
   */
  gfm?: boolean;

  /**
   * Conform to obscure parts of markdown.pl behavior
   * Don't fix original markdown bugs or poor behavior
   * Generally should stay false unless specific compatibility needed
   * Default: false
   */
  pedantic?: boolean;

  /**
   * Show HTML error message when rendering fails instead of throwing
   * Useful for user-generated content where errors shouldn't break page
   * Default: false
   */
  silent?: boolean;

  /**
   * Custom hooks for processing pipeline
   * See Hooks documentation for details
   * Default: null
   */
  hooks?: Hooks | null;

  /**
   * Custom renderer for generating output from tokens
   * Override to customize HTML output or create non-HTML formats
   * Default: new Renderer()
   */
  renderer?: Renderer | null;

  /**
   * Custom tokenizer for recognizing markdown syntax
   * Override methods to customize how markdown is recognized
   * Default: new Tokenizer()
   */
  tokenizer?: Tokenizer | null;

  /**
   * Custom extensions (populated by use())
   * Generally not set directly - use the use() method instead
   * Contains tokenizer and renderer extensions
   * Default: null
   */
  extensions?: {
    renderers: { [name: string]: RendererExtensionFunction };
    childTokens: { [name: string]: string[] };
    inline?: TokenizerExtensionFunction[];
    block?: TokenizerExtensionFunction[];
    startInline?: TokenizerStartFunction[];
    startBlock?: TokenizerStartFunction[];
  } | null;

  /**
   * Function called for each token, useful for preprocessing
   * Called after tokenization but before rendering
   * Can modify tokens in place
   * Default: null
   */
  walkTokens?: ((token: Token) => void | Promise<void> | (void | Promise<void>)[]) | null;
}

Usage Examples

Basic Options

import { marked } from "marked";

// Enable GFM line breaks
marked.setOptions({
  breaks: true,
  gfm: true
});

const html = marked.parse("Line 1\nLine 2");
// Output: <p>Line 1<br>Line 2</p>

// Without breaks: true, output would be: <p>Line 1\nLine 2</p>

Common Combinations:

  • GitHub-style: { gfm: true, breaks: true }
  • Strict CommonMark: { gfm: false, pedantic: false }
  • Original markdown.pl: { pedantic: true, gfm: false }

Async Mode

import { marked } from "marked";

// Enable async mode
marked.setOptions({ async: true });

// Now marked returns a Promise
const html = await marked.parse("# Async Markdown");

// Or use explicit async option
const html2 = await marked.parse("# Title", { async: true });

When to Use Async:

  • When using async walkTokens callbacks
  • When using async hooks (preprocess, postprocess)
  • When extensions perform async operations
  • When fetching external content during parsing

Async Performance Note: Async mode adds overhead even for sync operations. Only enable when needed.

Silent Mode

import { marked } from "marked";

// Default behavior (throw errors)
try {
  const html = marked.parse(malformedMarkdown);
} catch (err) {
  console.error('Parse error:', err);
}

// Silent mode (return error as HTML)
marked.setOptions({ silent: true });
const html = marked.parse(malformedMarkdown);
// Returns: <p>An error occurred:</p><pre>Error message</pre>

Silent Mode Use Cases:

  • User-generated content
  • Preview functionality
  • Graceful degradation
  • Error display to end users

Warning: Silent mode may hide legitimate bugs. Use sparingly.

Pedantic Mode

import { marked } from "marked";

// Conform to original markdown.pl behavior
marked.setOptions({
  pedantic: true,
  gfm: false
});

const html = marked.parse("# Title");

// Example differences:
// - Stricter emphasis rules
// - Different list handling
// - No tables, autolinks, or GFM features

Pedantic Mode Notes:

  • Enables markdown.pl compatibility bugs
  • Disables many conveniences
  • Rarely needed (only for legacy compatibility)
  • Conflicts with gfm: true

Custom Renderer

import { marked, Renderer } from "marked";

const renderer = new Renderer();

// Customize code block rendering
renderer.code = ({ text, lang, escaped }) => {
  const language = lang || 'text';
  return `<pre><code class="language-${language}">${text}</code></pre>\n`;
};

// Customize heading rendering
renderer.heading = ({ tokens, depth }) => {
  const text = this.parser.parseInline(tokens);
  const id = text.toLowerCase().replace(/[^\w]+/g, '-');
  return `<h${depth} id="${id}">${text}</h${depth}>\n`;
};

marked.setOptions({ renderer });

Renderer Customization Use Cases:

  • Syntax highlighting
  • Custom HTML attributes
  • Non-HTML output
  • Accessibility improvements
  • Custom styling

Multiple Options

import { marked } from "marked";

// Set multiple options at once
marked.setOptions({
  gfm: true,
  breaks: true,
  silent: false,
  async: false
});

// Options can be overridden per parse call
const html = marked.parse(markdown, {
  breaks: false // Override global setting
});

Option Precedence (highest to lowest):

  1. Parse-time options
  2. Extension options (via use())
  3. Global options (via setOptions())
  4. Default options

MarkedExtension Interface

Extensions provide a way to customize marked without modifying global options. Extensions can add custom tokenizers, renderers, and hooks.

interface MarkedExtension {
  /**
   * Enable async mode for this extension
   * Set to true if extension uses async operations
   */
  async?: boolean;

  /**
   * Enable GFM line breaks for this extension
   */
  breaks?: boolean;

  /**
   * Enable GFM features for this extension
   */
  gfm?: boolean;

  /**
   * Enable pedantic mode for this extension
   */
  pedantic?: boolean;

  /**
   * Enable silent mode for this extension
   */
  silent?: boolean;

  /**
   * Array of custom tokenizer and renderer extensions
   * Each extension adds new token types or overrides existing ones
   */
  extensions?: (TokenizerExtension | RendererExtension | (TokenizerExtension & RendererExtension))[] | null;

  /**
   * Partial hooks object to extend default hooks
   * Multiple extensions can define hooks - they chain together
   */
  hooks?: {
    preprocess?: (markdown: string) => string | Promise<string>;
    postprocess?: (html: string) => string | Promise<string>;
    processAllTokens?: (tokens: Token[] | TokensList) => Token[] | TokensList | Promise<Token[] | TokensList>;
    emStrongMask?: (src: string) => string;
    provideLexer?: () => Function;
    provideParser?: () => Function;
  } | null;

  /**
   * Partial renderer object to override default rendering methods
   * Return false from method to fall back to default renderer
   */
  renderer?: {
    [key: string]: (token: any) => string | false;
  } | null;

  /**
   * Partial tokenizer object to override default tokenization methods
   * Return false/undefined from method to fall back to default tokenizer
   */
  tokenizer?: {
    [key: string]: (src: string, ...args: any[]) => any;
  } | null;

  /**
   * Function to walk all tokens
   * Called for each token in the tree
   * Can modify tokens in place
   */
  walkTokens?: ((token: Token) => void | Promise<void>) | null;
}

Extension Usage

import { marked } from "marked";

// Using extension to customize behavior
marked.use({
  gfm: true,
  breaks: true,
  renderer: {
    heading({ tokens, depth }) {
      const text = this.parser.parseInline(tokens);
      return `<h${depth} class="custom-heading">${text}</h${depth}>\n`;
    },
    link({ href, title, tokens }) {
      const text = this.parser.parseInline(tokens);
      const titleAttr = title ? ` title="${title}"` : '';
      // Add target="_blank" to external links
      const external = href.startsWith('http') ? ' target="_blank" rel="noopener"' : '';
      return `<a href="${href}"${titleAttr}${external}>${text}</a>`;
    }
  },
  walkTokens(token) {
    if (token.type === 'link') {
      // Validate links
      if (!isValidUrl(token.href)) {
        console.warn('Invalid URL:', token.href);
      }
    }
  }
});

function isValidUrl(url) {
  try {
    new URL(url);
    return true;
  } catch {
    return false;
  }
}

Extension Benefits:

  • Don't modify global defaults
  • Can be applied conditionally
  • Composable (multiple extensions)
  • Isolated configuration

Getting Current Options

import { marked } from "marked";

// Get default options (original defaults, not current)
const defaults = marked.getDefaults();
console.log(defaults);
// Output:
// {
//   async: false,
//   breaks: false,
//   gfm: true,
//   pedantic: false,
//   silent: false,
//   hooks: null,
//   renderer: null,
//   tokenizer: null,
//   extensions: null,
//   walkTokens: null
// }

// Access current defaults (including modifications)
console.log(marked.defaults);
// Shows current default configuration

// Reset to original defaults
marked.setOptions(marked.getDefaults());

Configuration Inspection:

import { marked } from "marked";

// Check current configuration
function inspectConfig() {
  const config = marked.defaults;
  console.log('GFM enabled:', config.gfm);
  console.log('Breaks enabled:', config.breaks);
  console.log('Async mode:', config.async);
  console.log('Silent mode:', config.silent);
  console.log('Custom renderer:', config.renderer !== null);
  console.log('Custom hooks:', config.hooks !== null);
}

inspectConfig();

Option Priority

When using multiple option sources, they are applied in order with later options overriding earlier ones:

  1. Original defaults (from getDefaults())
  2. Global options (from setOptions())
  3. Extension options (from use())
  4. Parse-time options (passed to parse())
import { marked } from "marked";

// 1. Original defaults
// gfm: true, breaks: false

// 2. Set global default
marked.setOptions({ breaks: false, silent: false });

// 3. Use extension
marked.use({ breaks: true, async: false }); // breaks: true now overrides global

// 4. Parse-time option
const html = marked.parse('text', { breaks: false }); // breaks: false overrides extension

Priority Example:

import { marked } from "marked";

// Global: breaks=false
marked.setOptions({ breaks: false });

// Extension: breaks=true (overrides global)
marked.use({ breaks: true });

// Parse-time: breaks=false (overrides extension)
const html1 = marked.parse('Line 1\nLine 2', { breaks: false });
// Output: <p>Line 1\nLine 2</p> (no <br>)

// Without parse-time override, extension setting used
const html2 = marked.parse('Line 1\nLine 2');
// Output: <p>Line 1<br>Line 2</p> (has <br>)

Per-Instance Options

Create isolated instances with different configurations:

import { Marked } from "marked";

// Instance for GitHub-flavored markdown
const gfmMarked = new Marked({ 
  gfm: true, 
  breaks: true 
});

// Instance for strict CommonMark
const strictMarked = new Marked({ 
  gfm: false, 
  pedantic: false 
});

// Instance for pedantic markdown.pl compatibility
const pedanticMarked = new Marked({ 
  pedantic: true, 
  gfm: false 
});

// Each instance has independent options
const html1 = gfmMarked.parse('Line 1\nLine 2');
// Output: <p>Line 1<br>Line 2</p>

const html2 = strictMarked.parse('Line 1\nLine 2');
// Output: <p>Line 1\nLine 2</p>

const html3 = pedanticMarked.parse('Line 1\nLine 2');
// Output: <p>Line 1\nLine 2</p> (with pedantic quirks)

Instance Isolation:

import { Marked, marked } from "marked";

// Global marked configuration
marked.setOptions({ breaks: true });

// Create instance (doesn't inherit global config)
const instance = new Marked({ breaks: false });

// Global and instance are independent
const html1 = marked.parse('Line\nBreak'); // Has <br>
const html2 = instance.parse('Line\nBreak'); // No <br>

Option Validation

import { marked } from "marked";

// Valid options
marked.setOptions({
  gfm: true,
  breaks: true,
  silent: false
});

// Invalid/unknown options are ignored (no error)
marked.setOptions({
  unknownOption: true, // Ignored
  gfm: 'yes' // Type mismatch, may cause issues
});

// Type-safe with TypeScript
import type { MarkedOptions } from "marked";

const options: MarkedOptions = {
  gfm: true,
  breaks: true,
  // unknownOption: true // TypeScript error
};

marked.setOptions(options);

Common Option Patterns

Preview Mode (User Content)

import { marked } from "marked";
import DOMPurify from "dompurify";

// Safe parsing of untrusted content
marked.setOptions({
  silent: true,  // Don't throw on errors
  gfm: true,     // Support GFM features
  breaks: true   // Easier for users
});

function safeRender(markdown) {
  const html = marked.parse(markdown);
  return DOMPurify.sanitize(html);
}

Documentation Site

import { marked } from "marked";
import hljs from "highlight.js";

marked.use({
  gfm: true,
  breaks: false, // Strict paragraphs
  renderer: {
    code({ text, lang }) {
      if (lang && hljs.getLanguage(lang)) {
        const highlighted = hljs.highlight(text, { language: lang }).value;
        return `<pre><code class="hljs language-${lang}">${highlighted}</code></pre>\n`;
      }
      return false;
    },
    heading({ tokens, depth }) {
      const text = this.parser.parseInline(tokens);
      const id = text.toLowerCase().replace(/[^\w]+/g, '-');
      return `<h${depth} id="${id}"><a href="#${id}">#</a> ${text}</h${depth}>\n`;
    }
  }
});

Blog Posts

import { marked } from "marked";

marked.use({
  gfm: true,
  breaks: false,
  renderer: {
    link({ href, title, tokens }) {
      const text = this.parser.parseInline(tokens);
      const titleAttr = title ? ` title="${title}"` : '';
      // Open external links in new tab
      const target = href.startsWith('http') ? ' target="_blank" rel="noopener noreferrer"' : '';
      return `<a href="${href}"${titleAttr}${target}>${text}</a>`;
    },
    image({ href, title, text }) {
      const titleAttr = title ? ` title="${title}"` : '';
      return `<figure><img src="${href}" alt="${text}"${titleAttr} loading="lazy"><figcaption>${text}</figcaption></figure>`;
    }
  }
});

README Rendering

import { marked } from "marked";

// GitHub-like rendering
marked.use({
  gfm: true,
  breaks: false,
  renderer: {
    code({ text, lang }) {
      const language = lang || '';
      return `<div class="code-block"><div class="code-header">${language}</div><pre><code class="language-${language}">${text}</code></pre></div>\n`;
    },
    table(token) {
      // Add responsive wrapper
      const defaultHtml = false; // Get default rendering
      return `<div class="table-wrapper">${this.constructor.prototype.table.call(this, token)}</div>\n`;
    }
  }
});

Performance Tuning

import { marked } from "marked";

// For maximum performance
marked.setOptions({
  async: false,      // Sync is faster
  gfm: true,         // GFM is optimized
  breaks: false,     // Slightly faster
  silent: false,     // Don't catch errors
  walkTokens: null   // No token walking overhead
});

// For large documents
const largeMarkdown = '...'; // 10MB+

console.time('parse');
const html = marked.parse(largeMarkdown);
console.timeEnd('parse');

// Consider caching for repeated parses
const cache = new Map();
function cachedParse(markdown) {
  if (cache.has(markdown)) {
    return cache.get(markdown);
  }
  const html = marked.parse(markdown);
  if (cache.size < 100) { // Limit cache size
    cache.set(markdown, html);
  }
  return html;
}

Option Combinations to Avoid

import { marked } from "marked";

// Conflicting options
marked.setOptions({
  gfm: false,
  breaks: true  // breaks requires gfm, will not work
});

// Redundant async
marked.setOptions({
  async: true  // Not needed if no async operations
});

// Overly permissive
marked.setOptions({
  silent: true,  // May hide bugs
  pedantic: true // Usually unnecessary
});

Best Practices:

  • Only enable async when needed
  • Use silent sparingly (mainly for user content)
  • Prefer gfm: true unless strict CommonMark required
  • Avoid pedantic unless legacy compatibility needed
  • Test option combinations with your content

Debugging Configuration

import { marked } from "marked";

// Log current configuration
function debugConfig() {
  const config = marked.defaults;
  console.log('=== Marked Configuration ===');
  console.log('GFM:', config.gfm);
  console.log('Breaks:', config.breaks);
  console.log('Async:', config.async);
  console.log('Silent:', config.silent);
  console.log('Pedantic:', config.pedantic);
  console.log('Custom Renderer:', !!config.renderer);
  console.log('Custom Tokenizer:', !!config.tokenizer);
  console.log('Custom Hooks:', !!config.hooks);
  console.log('Extensions:', !!config.extensions);
  console.log('WalkTokens:', !!config.walkTokens);
  console.log('============================');
}

debugConfig();

// Test configuration with sample
function testConfig(markdown) {
  console.log('Input:', markdown);
  console.log('Output:', marked.parse(markdown));
  console.log('---');
}

testConfig('Line 1\nLine 2');
testConfig('~~strikethrough~~');
testConfig('https://example.com');