
Research
2025 Report: Destructive Malware in Open Source Packages
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.
markdown-flow-ui
Advanced tools
A React UI library for rendering markdown with interactive flow components, typewriter effects, and plugin support
React component library for rendering interactive MarkdownFlow documents with typewriter effects and real-time streaming.
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 | 简体中文
npm install markdown-flow-ui
# or
yarn add markdown-flow-ui
# or
pnpm add markdown-flow-ui
import { MarkdownFlow } from "markdown-flow-ui";
function App() {
return (
<MarkdownFlow
initialContentList={[
{
content:
"# Hello World\n\nThis is **MarkdownFlow** with typewriter effect!",
},
]}
disableTyping={false}
typingSpeed={30}
/>
);
}
import { MarkdownFlow } from "markdown-flow-ui";
function InteractiveExample() {
const content = `
Choose your language: ?[%{{lang}} English | 中文 | Español]
Your name: ?[%{{name}} Enter your name...]
?[Continue | Cancel]
`;
return (
<MarkdownFlow
initialContentList={[{ content }]}
onSend={(params) => {
console.log("User interaction:", params);
// Handle button clicks and input submissions
}}
/>
);
}
import { ScrollableMarkdownFlow } from "markdown-flow-ui";
import { useSSE } from "markdown-flow-ui";
function StreamingChat() {
const [messages, setMessages] = useState([]);
const { data, isConnected } = useSSE("/api/stream", {
onMessage: (chunk) => {
setMessages((prev) => {
const last = prev[prev.length - 1];
if (last && !last.isFinished) {
return [
...prev.slice(0, -1),
{ ...last, content: last.content + chunk },
];
}
return [...prev, { content: chunk, isFinished: false }];
});
},
});
return (
<ScrollableMarkdownFlow
height="500px"
initialContentList={messages}
onSend={(params) => {
// Send user input to backend
fetch("/api/chat", {
method: "POST",
body: JSON.stringify(params),
});
}}
/>
);
}
Main component for rendering markdown with typewriter effects.
interface MarkdownFlowProps {
initialContentList?: ContentItem[];
customRenderBar?: CustomRenderBarProps;
onSend?: (content: OnSendContentParams) => void;
typingSpeed?: number;
disableTyping?: boolean;
onBlockComplete?: (blockIndex: number) => void;
}
type ContentItem = {
content: string;
isFinished?: boolean;
defaultInputText?: string;
defaultButtonText?: string;
readonly?: boolean;
customRenderBar?: CustomRenderBarProps;
};
type OnSendContentParams = {
buttonText?: string;
variableName?: string;
inputText?: string;
};
Props:
initialContentList - Array of content blocks to rendertypingSpeed - Typing animation speed (default: 30ms/char)disableTyping - Disable typewriter effect (default: false)onSend - Callback for user interactionsonBlockComplete - Called when a block finishes typingcustomRenderBar - Custom component for additional UIExample:
<MarkdownFlow
initialContentList={[
{
content: "# Welcome\n\nChoose: ?[%{{choice}} A | B | C]",
isFinished: false,
},
]}
typingSpeed={50}
onSend={(params) => {
if (params.variableName === "choice") {
console.log("Selected:", params.buttonText);
}
}}
/>
Enhanced version with auto-scrolling and scroll management.
interface ScrollableMarkdownFlowProps extends MarkdownFlowProps {
height?: string | number;
className?: string;
}
Additional Props:
height - Container height (default: "100%")className - Additional CSS classesFeatures:
Example:
<ScrollableMarkdownFlow
height="400px"
initialContentList={messages}
onSend={handleUserMessage}
className="chat-container"
/>
Core component for rendering individual markdown blocks.
interface ContentRenderProps {
content: string;
customRenderBar?: CustomRenderBarProps;
onSend?: (content: OnSendContentParams) => void;
typingSpeed?: number;
disableTyping?: boolean;
defaultButtonText?: string;
defaultInputText?: string;
readonly?: boolean;
onTypeFinished?: () => void;
tooltipMinLength?: number;
}
Props:
content - Markdown content to rendertypingSpeed - Animation speed (default: 30)disableTyping - Disable animation (default: true)readonly - Make interactive elements read-onlyonTypeFinished - Called when typing completestooltipMinLength - Min length for tooltips (default: 10)Supported Markdown:
$E = mc^2$Custom Syntax:
# Buttons
?[Click me]
# Variable inputs
?[%{{userName}} Enter name...]
# Multiple choice
?[%{{color}} Red | Blue | Green]
# Mermaid diagrams
```mermaid
graph LR
A --> B
B --> C
```
Manages typewriter animation effects.
function useTypewriter(
content: string,
speed?: number,
disabled?: boolean
): {
displayText: string;
isComplete: boolean;
start: () => void;
pause: () => void;
reset: () => void;
};
Example:
const { displayText, isComplete, start, pause } = useTypewriter(
"Hello, World!",
50,
false
);
return (
<div>
<p>{displayText}</p>
{!isComplete && <button onClick={pause}>Pause</button>}
</div>
);
Auto-scroll management for containers.
function useScrollToBottom(
containerRef: RefObject<HTMLElement>,
dependencies: any[],
options?: {
behavior?: "smooth" | "auto";
autoScrollOnInit?: boolean;
scrollDelay?: number;
}
): {
showScrollToBottom: boolean;
handleUserScrollToBottom: () => void;
};
Example:
const containerRef = useRef(null);
const { showScrollToBottom, handleUserScrollToBottom } = useScrollToBottom(
containerRef,
[messages.length],
{ behavior: "smooth" }
);
return (
<div ref={containerRef}>
{messages.map((msg) => (
<div key={msg.id}>{msg.text}</div>
))}
{showScrollToBottom && (
<button onClick={handleUserScrollToBottom}>↓</button>
)}
</div>
);
Server-Sent Events integration.
function useSSE(
url: string,
options?: {
onMessage?: (data: any) => void;
onError?: (error: Error) => void;
onOpen?: () => void;
reconnect?: boolean;
reconnectInterval?: number;
}
): {
data: any;
isConnected: boolean;
error: Error | null;
close: () => void;
};
Example:
const { data, isConnected, error } = useSSE("/api/stream", {
onMessage: (chunk) => {
setContent((prev) => prev + chunk);
},
reconnect: true,
reconnectInterval: 3000,
});
// Content item in flow
type ContentItem = {
content: string;
isFinished?: boolean;
defaultInputText?: string;
defaultButtonText?: string;
readonly?: boolean;
customRenderBar?: CustomRenderBarProps;
};
// User interaction parameters
type OnSendContentParams = {
buttonText?: string;
variableName?: string;
inputText?: string;
};
// Custom render bar component
type CustomRenderBarProps = React.ComponentType<{
content?: string;
onSend?: (content: OnSendContentParams) => void;
displayContent: string;
}>;
// All component props are exported
import type {
MarkdownFlowProps,
ScrollableMarkdownFlowProps,
ContentRenderProps,
} from "markdown-flow-ui";
Custom Variable Plugin:
Handles interactive buttons and inputs.
?[Button Text] # Simple button
?[%{{variable}} Placeholder...] # Input field
?[%{{choice}} A | B | C] # Multiple choice
Mermaid Plugin:
Renders diagrams using Mermaid.
```mermaid
graph TD
A[Start] --> B[Process]
B --> C[End]
```
// Define plugin component
const CustomPlugin: React.FC<{ value: string; type?: string }> = ({
value,
type = 'default'
}) => {
return (
<div className="custom-plugin">
<span>{type}: {value}</span>
</div>
);
};
// Register with ContentRender
const components = {
'custom-element': CustomPlugin,
};
The library uses Tailwind CSS and provides customization through:
CSS Classes:
.markdown-flow {
}
.content-render {
}
.content-render-table {
}
.content-render-ol {
}
.content-render-ul {
}
.scrollable-markdown-container {
}
.scroll-to-bottom-btn {
}
CSS Variables:
:root {
--markdown-flow-primary: #2563eb;
--markdown-flow-background: #ffffff;
--markdown-flow-text: #1f2937;
--markdown-flow-border: #d1d5db;
--markdown-flow-code-bg: #f3f4f6;
}
Component Classes:
<MarkdownFlow className="my-custom-flow" />
<ScrollableMarkdownFlow className="chat-interface" />
const CustomBar: CustomRenderBarProps = ({ displayContent, onSend }) => {
return (
<div className="flex gap-2 mt-4">
<button
onClick={() => onSend({ buttonText: "Regenerate" })}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
Regenerate
</button>
<button
onClick={() => navigator.clipboard.writeText(displayContent)}
className="px-4 py-2 bg-gray-500 text-white rounded"
>
Copy
</button>
</div>
);
};
<MarkdownFlow customRenderBar={CustomBar} initialContentList={messages} />;
const StreamingChat = () => {
const [content, setContent] = useState("");
useSSE("/api/stream", {
onMessage: (data) => {
setContent((prev) => prev + data.chunk);
},
});
return (
<ScrollableMarkdownFlow
initialContentList={[{ content, isFinished: false }]}
disableTyping={false}
typingSpeed={20}
/>
);
};
const Conversation = () => {
const [blocks, setBlocks] = useState([
{ content: "# Assistant\n\nHello! How can I help?", isFinished: true },
{
content: "What would you like to know?\n\n?[%{{topic}} Enter topic...]",
isFinished: false,
},
]);
const handleSend = (params) => {
if (params.variableName === "topic") {
setBlocks((prev) => [
...prev,
{ content: `You asked about: ${params.inputText}`, isFinished: false },
]);
}
};
return (
<MarkdownFlow
initialContentList={blocks}
onSend={handleSend}
onBlockComplete={(index) => {
setBlocks((prev) =>
prev.map((b, i) => (i === index ? { ...b, isFinished: true } : b))
);
}}
/>
);
};
markdown-flow-ui is part of the MarkdownFlow ecosystem for creating personalized, AI-driven interactive documents:
MIT License - see LICENSE file for details.
FAQs
A React UI library for rendering markdown with interactive flow components, typewriter effects, and plugin support
The npm package markdown-flow-ui receives a total of 2,204 weekly downloads. As such, markdown-flow-ui popularity was classified as popular.
We found that markdown-flow-ui demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.

Research
/Security News
A five-month operation turned 27 npm packages into durable hosting for browser-run lures that mimic document-sharing portals and Microsoft sign-in, targeting 25 organizations across manufacturing, industrial automation, plastics, and healthcare for credential theft.