A remark plugin library for parsing MarkdownFlow documents
MarkdownFlow (also known as MDFlow or markdown-flow) extends standard Markdown with AI to create personalized, interactive pages. Its tagline is “Write Once, Deliver Personally”.
npm install remark-flow
# or
yarn add remark-flow
# or
pnpm add remark-flow
Basic Usage
import { remark } from 'remark';
import remarkFlow from 'remark-flow';
const processor = remark().use(remarkFlow);
const markdown = `
# Welcome to Interactive Content!
Choose one option: ?[Option A | Option B | Option C]
Choose multiple skills: ?[%{{skills}} JavaScript||TypeScript||Python]
Enter your name: ?[%{{username}}...Please enter your name]
`;
const result = processor.processSync(markdown);
// Each ?[...] becomes a structured custom-variable node in the AST
Advanced Usage
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkFlow from 'remark-flow';
import remarkStringify from 'remark-stringify';
const processor = unified()
.use(remarkParse)
.use(remarkFlow)
.use(remarkStringify);
const result = processor.processSync(`
Select theme: ?[%{{theme}} Light//light | Dark//dark | ...custom]
Action: ?[Save Changes//save | Cancel//cancel]
`);
🧩 Supported Syntax Patterns
1. Simple Buttons
?[Submit]
?[Continue | Cancel]
?[Yes | No | Maybe]
| = Single-select mode, || becomes part of button values
|| = Multi-select mode, | becomes part of button values
First separator type wins and determines the entire parsing behavior
8. Unicode & International Support
?[%{{语言}} English//en | 中文//zh | 日本語//ja]
?[%{{用户名}}...请输入您的姓名]
?[👍 Good | 👎 Bad | 🤔 Unsure]
📖 API Reference
Plugin Exports
// Default export (recommended)
import remarkFlow from 'remark-flow';
// Named exports
import {
remarkFlow, // Main plugin, functionally the same as the default export
remarkInteraction, // The core plugin, which is also the default export
remarkCustomVariable, // Variable-focused plugin
createInteractionParser, // Parser factory
InteractionType, // Type enums
} from 'remark-flow';
Output Format
All plugins transform ?[...] syntax into custom-variable AST nodes:
import { createInteractionParser, InteractionType } from 'remark-flow';
const parser = createInteractionParser();
// Parse content and get detailed result
const result = parser.parse('?[%{{theme}} Light | Dark]');
// Parse and convert to remark-compatible format
const remarkData = parser.parseToRemarkFormat('?[%{{theme}} Light | Dark]');
🔗 Usage Examples
remark-flow can be used in two main ways:
Standalone - Parse and transform syntax, then render with your own UI components
With markdown-flow-ui - Use the pre-built React components for instant interactive UI
🎯 Standalone Usage (Custom Rendering)
When using remark-flow standalone, you parse the syntax and create your own UI components based on the AST nodes.
Basic AST Transformation
import { remark } from 'remark';
import { visit } from 'unist-util-visit';
import remarkFlow from 'remark-flow';
import type { Node } from 'unist';
const processor = remark().use(remarkFlow);
const markdown = `
# Choose Your Preferences
Select language: ?[%{{language}} JavaScript | Python | TypeScript | Go]
Enter your name: ?[%{{username}}...Your full name]
Action: ?[Save//save | Cancel//cancel]
`;
// Parse and examine the AST
const ast = processor.parse(markdown);
processor.runSync(ast);
// Find custom-variable nodes
visit(ast, 'custom-variable', (node: any) => {
console.log('Found interaction:', node.data);
// Output: { variableName: 'language', buttonTexts: ['JavaScript', 'Python', 'TypeScript', 'Go'], buttonValues: [...] }
});
Custom HTML Renderer
import { visit } from 'unist-util-visit';
import { remark } from 'remark';
import remarkHtml from 'remark-html';
function createCustomRenderer() {
return (tree: Node) => {
visit(tree, 'custom-variable', (node: any) => {
const { variableName, buttonTexts, buttonValues, placeholder } =
node.data;
if (buttonTexts && buttonTexts.length > 0) {
// Render as button group
const buttonsHtml = buttonTexts
.map((text, i) => {
const value = buttonValues?.[i] || text;
return `<button onclick="selectOption('${variableName}', '${value}')" class="interactive-btn">
${text}
</button>`;
})
.join('');
node.type = 'html';
node.value = `
<div class="button-group" data-variable="${variableName}">
${buttonsHtml}
</div>
`;
} else if (placeholder) {
// Render as text input
node.type = 'html';
node.value = `
<div class="input-group">
<label for="${variableName}">${placeholder}</label>
<input
id="${variableName}"
name="${variableName}"
placeholder="${placeholder}"
class="interactive-input"
/>
</div>
`;
}
});
};
}
// Use with remark processor
const processor = remark()
.use(remarkFlow)
.use(createCustomRenderer)
.use(remarkHtml);
const result = processor.processSync(markdown);
console.log(result.toString()); // HTML with custom interactive elements
Remark Flow
A remark plugin library for parsing MarkdownFlow documents
MarkdownFlow (also known as MDFlow or markdown-flow) extends standard Markdown with AI to create personalized, interactive pages. Its tagline is “Write Once, Deliver Personally”.
English | 简体中文
🚀 Quick Start
Install
Basic Usage
Advanced Usage
🧩 Supported Syntax Patterns
1. Simple Buttons
Output:
{ buttonTexts: ["Yes", "No", "Maybe"], buttonValues: ["Yes", "No", "Maybe"], isMultiSelect: false }2. Custom Button Values
Output:
{ buttonTexts: ["Save Changes"], buttonValues: ["save-action"] }3. Variable Text Input
Output:
{ variableName: "username", placeholder: "Enter your name" }4. Variable Button Selection (Single-Select)
Output:
{ variableName: "theme", buttonTexts: ["Light", "Dark"], buttonValues: ["Light", "Dark"], isMultiSelect: false }5. Variable Button Selection (Multi-Select)
Output:
{ variableName: "skills", buttonTexts: ["JavaScript", "TypeScript", "Python"], buttonValues: ["JavaScript", "TypeScript", "Python"], isMultiSelect: true }6. Combined: Buttons + Text Input
Output:
7. Separator Priority Rules
The first separator type encountered determines the parsing mode:
Key Points:
|= Single-select mode,||becomes part of button values||= Multi-select mode,|becomes part of button values8. Unicode & International Support
📖 API Reference
Plugin Exports
Output Format
All plugins transform
?[...]syntax intocustom-variableAST nodes:Parser API
🔗 Usage Examples
remark-flow can be used in two main ways:
🎯 Standalone Usage (Custom Rendering)
When using remark-flow standalone, you parse the syntax and create your own UI components based on the AST nodes.
Basic AST Transformation
Custom HTML Renderer
React Custom Components
🎨 With markdown-flow-ui (Pre-built Components)
For a complete React component library with ready-to-use interactive components, use markdown-flow-ui.
Basic Integration
For advanced examples with streaming, multi-step forms, and more features, see:
📊 Comparison: Standalone vs markdown-flow-ui
🌐 MarkdownFlow Ecosystem
remark-flow is part of the MarkdownFlow ecosystem for creating personalized, AI-driven interactive documents:
💖 Sponsors
AI-Shifu.com
AI-powered personalized learning platform
📄 License
MIT License - see LICENSE file for details.
🙏 Acknowledgments
📞 Support