Skip to content

Dual-Engine Architecture

Incremark features a dual-engine parsing system — an important architectural decision we made during development. We want to give users the freedom to choose: find the perfect balance between extreme performance and perfect compatibility for your specific use case.

Why Dual Engines?

During Incremark's development, we faced a core question: How do we achieve the best performance in streaming AI scenarios?

After extensive research and testing, we discovered:

  • Marked is extremely fast, but doesn't natively support footnotes, math, and other advanced features
  • Micromark has perfect spec compliance and a rich plugin ecosystem, but has a larger bundle size

Our decision: Why not both?

With the dual-engine architecture, users can choose based on their needs:

  • Performance-sensitive AI chat scenarios → Use the Marked engine
  • Documents requiring strict spec compliance → Use the Micromark engine

Engine Overview

EngineSpeedFeaturesBundle SizeBest For
Marked (Default)⚡⚡⚡⚡⚡Standard + Enhanced ExtensionsSmallerReal-time streaming, AI chat
Micromark⚡⚡⚡Full CommonMark + PluginsLargerComplex documents, strict compliance

Marked Engine (Default)

The Marked engine is our default choice, deeply optimized for streaming AI scenarios.

Why Marked as the Default?

  1. Extreme parsing speed: Marked is one of the fastest Markdown parsers in the JavaScript ecosystem
  2. Battle-tested stability: Over 10 years of history, validated by countless projects
  3. Easy to extend: Flexible extension mechanism allows us to add features as needed
  4. Small bundle size: Benefits frontend tree-shaking optimization

What We Enhanced for Marked

Native Marked is a "good enough" parser focused on standard Markdown syntax, without many advanced features. But in AI scenarios, we often need these features.

Therefore, Incremark extends Marked with custom extensions:

FeatureNative MarkedIncremark EnhancedDescription
Footnotes❌ Not supported✅ Full GFM footnotes[^1] references and [^1]: content definitions
Math Blocks❌ Not supported✅ Inline and block math$E=mc^2$ and $$...$$
Custom Containers❌ Not supported✅ Directive syntax:::tip, :::warning, :::danger
Inline HTML Parsing⚠️ Preserved as-is✅ Structured parsingParses HTML into manipulable AST nodes
Optimistic References❌ Not supported✅ Streaming-friendlyGracefully handles incomplete links/images during streaming
Footnote Definition Blocks❌ Not supported✅ Multi-line contentSupports complex footnotes with code blocks, lists, etc.

💡 These extensions are carefully designed for AI scenarios. They provide full functionality while minimizing performance overhead.

Usage

The Marked engine is the default, so you don't need any special configuration:

vue
<script setup>
import { ref } from 'vue'
import { IncremarkContent } from '@incremark/vue'

const content = ref('')
const isFinished = ref(false)
</script>

<template>
  <!-- Marked engine is used by default -->
  <IncremarkContent 
    :content="content" 
    :is-finished="isFinished"
  />
</template>

Enable/Disable Specific Features

vue
<template>
  <IncremarkContent 
    :content="content" 
    :is-finished="isFinished"
    :incremark-options="{
      gfm: true,        // GFM extensions (tables, strikethrough, etc.)
      math: true,       // Math formulas
      containers: true, // Custom containers
      htmlTree: true    // HTML structured parsing
    }"
  />
</template>

Micromark Engine

The Micromark engine is the choice for perfect spec compliance.

Why Offer Micromark?

While the Marked engine satisfies most scenarios, some users may have stricter requirements:

  1. Strict CommonMark compliance: Micromark is currently the most spec-compliant parser
  2. Rich plugin ecosystem: GFM, Math, Directive plugins are all community-polished
  3. Precise position information: AST nodes include accurate line/column positions for error locating
  4. Better edge case handling: More stable in complex nested scenarios

Usage

To use the Micromark engine, you need to import MicromarkAstBuilder and pass it via astBuilder option:

ts
// In your composable or setup
import { createIncremarkParser } from '@incremark/core'
import { MicromarkAstBuilder } from '@incremark/core/engines/micromark'

const parser = createIncremarkParser({
  astBuilder: MicromarkAstBuilder,
  gfm: true,
  math: true
})

Note: The IncremarkContent component currently uses the Marked engine by default. To use Micromark with the component, you would need to use useIncremark directly with a custom parser.

When Should You Use Micromark?

  • Your content includes complex nested structures
  • You need to handle edge cases that Marked can't parse correctly
  • Your application has strict CommonMark compliance requirements
  • You need Micromark plugins beyond our built-in extensions

Complete Benchmark Data

We benchmarked 38 real Markdown files. Here are the complete results:

Test Environment

  • Test files: 38 files, 6,484 lines total, 128.55 KB
  • Test method: Simulated streaming input, character-by-character append
  • Compared solutions: Streamdown, markstream-vue, ant-design-x

Full Test Results

FilenameLinesSize(KB)IncremarkStreamdownmarkstreamant-design-xvs Streamdownvs markstreamvs ant-design-x
test-footnotes-simple.md150.090.3 ms0.0 ms1.4 ms0.2 ms0.1x4.7x0.6x
simple-paragraphs.md160.410.9 ms0.9 ms5.9 ms1.0 ms1.1x6.7x1.2x
test-footnotes-multiline.md210.180.6 ms0.0 ms2.2 ms0.4 ms0.1x3.5x0.6x
test-footnotes-edge-cases.md270.250.8 ms0.0 ms4.2 ms1.2 ms0.0x5.3x1.5x
test-footnotes-complex.md280.242.1 ms0.0 ms4.8 ms1.0 ms0.0x2.3x0.5x
introduction.md341.575.6 ms12.6 ms75.6 ms12.8 ms2.2x13.4x2.3x
devtools.md510.921.2 ms0.9 ms6.1 ms1.1 ms0.8x5.0x0.9x
footnotes.md520.941.7 ms0.2 ms10.6 ms1.9 ms0.1x6.3x1.2x
html-elements.md551.021.6 ms2.2 ms12.6 ms2.8 ms1.4x7.8x1.7x
themes.md580.961.9 ms1.3 ms8.6 ms1.8 ms0.7x4.4x0.9x
test-footnotes-comprehensive.md630.665.6 ms0.1 ms25.8 ms7.7 ms0.0x4.6x1.4x
auto-scroll.md721.683.9 ms3.5 ms39.9 ms4.9 ms0.9x10.1x1.2x
custom-codeblocks.md721.443.4 ms2.0 ms14.9 ms2.5 ms0.6x4.4x0.7x
custom-components.md731.404.0 ms2.0 ms32.7 ms2.9 ms0.5x8.1x0.7x
custom-containers.md881.674.2 ms2.4 ms18.1 ms3.1 ms0.6x4.3x0.7x
typewriter.md881.895.6 ms4.1 ms35.0 ms4.9 ms0.7x6.2x0.9x
concepts.md914.2912.0 ms50.5 ms381.9 ms53.6 ms4.2x31.9x4.5x
INLINE_CODE_UPDATE.md941.664.7 ms17.2 ms60.9 ms15.6 ms3.7x12.9x3.3x
comparison.md1095.3920.5 ms74.0 ms552.2 ms85.2 ms3.6x26.9x4.1x
basic-usage.md1303.048.5 ms12.3 ms74.1 ms14.1 ms1.4x8.7x1.7x
CODE_BACKGROUND_SEPARATION.md1312.838.7 ms28.8 ms153.6 ms31.3 ms3.3x17.6x3.6x
P2_SUMMARY.md1382.618.3 ms38.4 ms157.2 ms41.9 ms4.6x18.9x5.0x
quick-start.md1463.047.3 ms7.3 ms64.2 ms9.6 ms1.0x8.8x1.3x
complex-html-examples.md1473.999.0 ms58.8 ms279.3 ms57.2 ms6.6x31.1x6.4x
CODE_COLOR_SEPARATION.md1623.5110.0 ms32.8 ms191.1 ms36.9 ms3.3x19.1x3.7x
P0_OPTIMIZATION_REPORT.md1683.5310.1 ms56.2 ms228.0 ms58.1 ms5.6x22.6x5.8x
COLOR_SYSTEM_REFACTOR.md1693.7818.5 ms64.0 ms355.5 ms69.1 ms3.5x19.2x3.7x
FOOTNOTE_TEST_GUIDE.md2192.8712.3 ms0.2 ms167.6 ms45.0 ms0.0x13.7x3.7x
P2_COLORS_PACKAGE_REPORT.md2264.1011.4 ms77.9 ms311.6 ms80.5 ms6.8x27.2x7.0x
FOOTNOTE_FIX_SUMMARY.md2363.9322.7 ms0.5 ms535.0 ms120.8 ms0.0x23.6x5.3x
BASE_COLORS_SYSTEM.md2594.4735.8 ms43.0 ms191.8 ms43.4 ms1.2x5.4x1.2x
OPTIMIZATION_COMPARISON.md2705.4217.8 ms52.3 ms366.1 ms61.9 ms2.9x20.6x3.5x
P1_OPTIMIZATION_REPORT.md3275.6320.7 ms106.8 ms433.8 ms114.8 ms5.2x21.0x5.5x
OPTIMIZATION_PLAN.md3716.8933.1 ms67.6 ms372.1 ms76.7 ms2.0x11.2x2.3x
OPTIMIZATION_SUMMARY.md3916.2419.1 ms208.4 ms980.6 ms217.8 ms10.9x51.3x11.4x
P1.5_COLOR_SYSTEM_REPORT.md4829.1222.0 ms145.5 ms789.8 ms168.2 ms6.6x35.9x7.7x
BLOCK_TRANSFORMER_ANALYSIS.md4899.2475.7 ms574.3 ms1984.1 ms619.9 ms7.6x26.2x8.2x
test-md-01.md91617.6787.7 ms1441.1 ms5754.7 ms1656.9 ms16.4x65.6x18.9x
【Total】6484128.55519.4 ms3190.3 ms14683.9 ms3728.6 ms6.1x28.3x7.2x

How to Interpret This Data

We're Honest: Incremark is Slower in Some Scenarios

You may notice that for test-footnotes-*.md and FOOTNOTE_*.md files, Incremark is much slower than Streamdown (0.0x - 0.1x).

The reason is simple: Streamdown doesn't support footnote syntax.

When Streamdown encounters [^1] footnote references, it simply skips them. Meanwhile, Incremark:

  1. Recognizes footnote references
  2. Parses footnote definition blocks (which may contain multi-line content, code blocks, lists, etc.)
  3. Establishes reference relationships
  4. Generates correct AST structure

This isn't a performance issue — it's a feature difference. We believe complete footnote support is crucial for AI scenarios, so we chose to implement it.

Where's the Real Performance Advantage?

Excluding footnote-related files, look at standard Markdown content performance:

FileLinesIncremarkStreamdownAdvantage
concepts.md9112.0 ms50.5 ms4.2x
comparison.md10920.5 ms74.0 ms3.6x
complex-html-examples.md1479.0 ms58.8 ms6.6x
P0_OPTIMIZATION_REPORT.md16810.1 ms56.2 ms5.6x
OPTIMIZATION_SUMMARY.md39119.1 ms208.4 ms10.9x
test-md-01.md91687.7 ms1441.1 ms16.4x

Conclusion: For standard Markdown content, the larger the document, the more pronounced Incremark's advantage.

Why Such a Gap?

This is the direct result of O(n) vs O(n²) algorithmic complexity.

Traditional parsers (Streamdown, ant-design-x, markstream-vue) re-parse the entire document on every new chunk:

Chunk 1: Parse 100 chars
Chunk 2: Parse 200 chars (100 old + 100 new)
Chunk 3: Parse 300 chars (200 old + 100 new)
...
Chunk 100: Parse 10,000 chars

Total work: 100 + 200 + 300 + ... + 10000 = 5,050,000 character operations

Incremark's incremental parsing only processes new content:

Chunk 1: Parse 100 chars → cache stable blocks
Chunk 2: Parse only ~100 new chars
Chunk 3: Parse only ~100 new chars
...
Chunk 100: Parse only ~100 new chars

Total work: 100 × 100 = 10,000 character operations

That's a 500x difference. This is why an 18KB document can be 16x+ faster.

Feature Parity

We strive to keep both engines functionally consistent:

FeatureMarked EngineMicromark Engine
GFM (Tables, Strikethrough, Autolinks)
Math Blocks ($...$ and $$...$$)
Custom Containers (:::tip, etc.)
HTML Element Parsing
Footnotes
Typewriter Animation
Incremental Updates

Switching Engines

Engine selection is done at initialization time, not runtime. This is by design for tree-shaking optimization.

Why Not Runtime Switching?

To ensure optimal bundle size:

  • Default import only includes the marked engine
  • micromark engine is imported separately when needed
  • This allows bundlers to tree-shake unused engines

How to Switch Engines

vue
<script setup>
import { ref } from 'vue'
import { IncremarkContent } from '@incremark/vue'

const content = ref('')
const isFinished = ref(false)

// Engine is selected at initialization
// For marked (default): no extra import needed
// For micromark: import MicromarkAstBuilder
</script>

<template>
  <!-- Uses marked engine by default -->
  <IncremarkContent 
    :content="content" 
    :is-finished="isFinished"
    :incremark-options="{ gfm: true, math: true }"
  />
</template>

Using Micromark Engine

To use micromark, import MicromarkAstBuilder from the separate engine entry:

ts
import { createIncremarkParser } from '@incremark/core'
import { MicromarkAstBuilder } from '@incremark/core/engines/micromark'

// Create parser with micromark engine
const parser = createIncremarkParser({
  astBuilder: MicromarkAstBuilder,
  gfm: true,
  math: true
})

⚠️ Tree-shaking Note: Importing from @incremark/core/engines/micromark only adds micromark to your bundle. The default import keeps only marked.

Extending Engines

Both engines support custom extensions. See the Extensions Guide for details.

ts
// Custom marked extension example
import { createCustomExtension } from '@incremark/core'

const myExtension = createCustomExtension({
  name: 'myPlugin',
  // ... extension config
})

Summary and Recommendations

AspectMarkedMicromark
Parsing Speed⚡⚡⚡⚡⚡⚡⚡⚡
Bundle Size📦 Smaller📦 Larger
CommonMark Compliance✅ Good✅ Perfect
Built-in Extensions✅ Footnotes, Math, Containers✅ Via plugins
Plugin Ecosystem🔧 Growing🔧 Mature
Recommended ForStreaming AI, Real-time renderingStatic documents, Strict compliance

Our Recommendations:

  1. Most scenarios: Use the default Marked engine — it's already great
  2. Parsing issues: If Marked doesn't handle certain edge cases well, try switching to Micromark
  3. Extreme performance needs: Marked engine is your best choice
  4. Strict compliance needs: Micromark engine is more suitable

We'll continue optimizing both engines to ensure they provide the best experience for you.