Lightweight, block-based markdown solution (zero dependencies). Parse, create, serialize, and render markdown with TypeScript support.
| Package | Version | Description |
|---|---|---|
| @create-markdown/core | 0.2.0 | Zero-dependency parsing and serialization |
| @create-markdown/react | 0.2.0 | React components and hooks |
| @create-markdown/preview | 1.0.0 | HTML rendering with themes, plugins, and BYO-parser support |
| @create-markdown/mdx | 0.2.0 | MDX conversion |
| create-markdown | 0.4.0 | Convenience bundle |
- Block-based architecture: Work with structured blocks instead of raw strings
- Bidirectional conversion: Parse markdown to blocks, serialize blocks to markdown
- Rich inline styles: Bold, italic, code, links, strikethrough, highlights
- React components: Optional React bindings for rendering and editing
- HTML preview: Framework-agnostic HTML rendering with themes
- BYO parser: Use
applyPreviewTheme()withmarked,markdown-it,remark, or any parser -- no lock-in to@create-markdown/core - CSS custom property theming:
system.csstheme integrates with any design system via--cm-*variables - Syntax highlighting: Shiki plugin for code blocks
- Diagrams: Mermaid plugin for flowcharts, sequence diagrams, etc.
- Web Component:
<markdown-preview>custom element with optional light DOM mode - BYO sanitizer: Plug in DOMPurify or any sanitizer function
- Theme CSS as strings: Import theme CSS as string constants for CSS-in-JS or web components
- Zero dependencies: Core package has no runtime dependencies
- Full TypeScript: Complete type definitions with generics
# Install individual packages (recommended)
pnpm add @create-markdown/core
pnpm add @create-markdown/react
pnpm add @create-markdown/preview
# Or install the convenience bundle
pnpm add create-markdownimport { parse, stringify, h1, paragraph } from '@create-markdown/core';
// Parse markdown to blocks
const blocks = parse('# Hello\n\nWorld');
// Create blocks programmatically
const doc = [
h1('Hello'),
paragraph('World'),
];
// Serialize back to markdown
const markdown = stringify(doc);@create-markdown/preview works with any parser's HTML output -- no need to switch to @create-markdown/core:
import { applyPreviewTheme } from '@create-markdown/preview';
import { marked } from 'marked';
const raw = marked.parse('# Hello\n\nSome **bold** text.');
const themed = applyPreviewTheme(raw); // wraps elements with cm-* classesPair with any theme CSS (github.css, github-dark.css, minimal.css, or system.css) and the styled output just works.
Sanitize untrusted HTML before assigning the themed output to the DOM.
import { BlockRenderer, useDocument, paragraph } from '@create-markdown/react';
function Editor() {
const doc = useDocument([paragraph('Start typing...')]);
return (
<div>
<BlockRenderer blocks={doc.blocks} />
<button onClick={() => doc.appendBlock(paragraph('New paragraph'))}>
Add Paragraph
</button>
</div>
);
}import { renderAsync, shikiPlugin, mermaidPlugin } from '@create-markdown/preview';
import { parse } from '@create-markdown/core';
const blocks = parse(`
# Code Example
\`\`\`typescript
const x = 42;
\`\`\`
\`\`\`mermaid
flowchart LR
A --> B --> C
\`\`\`
`);
const html = await renderAsync(blocks, {
plugins: [shikiPlugin(), mermaidPlugin()],
});Preview output is intended for trusted content by default. If the markdown or generated HTML can come from users, sanitize it before rendering.
The system.css theme uses CSS custom properties so it adapts to any design system:
/* Set once -- the theme adapts to light and dark mode automatically */
:root {
--cm-text: #1f2328;
--cm-bg: #ffffff;
--cm-border: #d1d9e0;
--cm-code-bg: #f6f8fa;
--cm-link: #0969da;
}
@media (prefers-color-scheme: dark) {
:root {
--cm-text: #e6edf3;
--cm-bg: #0d1117;
--cm-border: #30363d;
--cm-code-bg: #161b22;
--cm-link: #58a6ff;
}
}import '@create-markdown/preview/themes/system.css';Pass any sanitizer function when rendering untrusted content:
import { blocksToHTML } from '@create-markdown/preview';
import DOMPurify from 'dompurify';
const html = blocksToHTML(blocks, {
sanitize: (html) => DOMPurify.sanitize(html, { USE_PROFILES: { html: true } }),
});Import theme CSS as string constants for CSS-in-JS, bundlers that struggle with CSS imports, or web components:
import { themes } from '@create-markdown/preview/themes';
// themes.github, themes.githubDark, themes.minimal, themes.system
const style = document.createElement('style');
style.textContent = themes.githubDark;
document.head.appendChild(style);<script type="module">
import { registerPreviewElement } from '@create-markdown/preview';
registerPreviewElement();
</script>
<markdown-preview theme="github-dark">
# Hello World
This renders automatically!
</markdown-preview>Use shadowMode: 'none' to render in the light DOM and inherit page styles:
registerPreviewElement({ shadowMode: 'none' });The web component also assumes trusted markdown by default, so sanitize user-provided content before passing it in.
| Document | Description |
|---|---|
| packages/core/README.md | Core API reference |
| packages/react/README.md | React components guide |
| packages/preview/README.md | Preview and plugins guide |
| ROADMAP.md | Feature roadmap |
| CONTRIBUTING.md | Contribution guidelines |
| INTEGRATION.md | Framework integrations |
# Install dependencies
pnpm install
# Build all packages
pnpm run build
# Run tests
pnpm run test
# Type check
pnpm run typecheck
# Run the playground
pnpm run playgroundcreate-markdown/
├── packages/
│ ├── core/ # @create-markdown/core
│ ├── react/ # @create-markdown/react
│ ├── preview/ # @create-markdown/preview
│ ├── mdx/ # @create-markdown/mdx
│ ├── create-markdown/ # Convenience bundle
│ └── docs/ # Documentation site
├── playground/ # Demo application
├── scripts/ # Release and utility scripts
└── .github/ # CI/CD workflows
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
MIT