Markpad is an offline-first Markdown workspace with live preview, reusable templates, frontmatter variables, theme presets, and URL-based publishing.
Live app: markpad.cc
- Why Markpad
- Core Features
- Tech Stack
- Getting Started
- Environment Variables
- Development Commands
- Project Structure
- How Themes Work
- Template and Variable System
- Publishing Model
- Contributing
- License
Markpad is designed for people who write structured Markdown repeatedly, such as reports, invoices, notes, docs, and content drafts.
It combines:
- A fast editor with split preview
- Data-driven writing with frontmatter and variables
- Reusable templates and visual themes
- Local persistence without requiring an account
- Live split preview while typing
- Markdown editing with syntax highlighting
- GitHub Flavored Markdown support via
remark-gfm - Toolbar actions for common formatting
- Light and dark mode support
- 25+ built-in themes (serif, sans-serif, monospace, accessibility, experimental)
- Gallery with category filters, search, and favorites
- Custom theme editor for creating and saving local themes
- IndexedDB persistence (documents survive refreshes)
- Document states: starred, recent, trash
- System templates + user templates
- Variable schemas for template-driven documents
- YAML frontmatter parsing
- Variable interpolation with
{{variable}}and nested paths ({{company.name}}) - Nunjucks blocks:
- Conditionals:
{% if %} ... {% endif %} - Loops:
{% for %} ... {% endfor %}
- Conditionals:
- Import from local
.mdfiles - Import from URL (via markclipper service)
- Export to Markdown, styled HTML, and PDF
- Publish as a shareable URL-encoded page
| Layer | Tools |
|---|---|
| Framework | React 18 + TypeScript |
| Build | Vite 6 |
| Styling | Tailwind CSS 3 + @tailwindcss/typography |
| Markdown | react-markdown + remark-gfm |
| Templating | Nunjucks |
| Frontmatter | gray-matter + js-yaml |
| Persistence | IndexedDB (idb) |
| Testing | Vitest + Testing Library |
| Component Dev | Storybook 8 (@storybook/react-vite) |
| Package Manager | pnpm 10 |
- Node.js 20+
- pnpm 10+
git clone https://github.com/markpad/markpad.git
cd markpad
pnpm install
pnpm devThen open http://localhost:3000.
Create .env.local in the project root (all optional):
# URL of the web clipper API
# Falls back to hosted markclipper if omitted
VITE_CLIPPER_URL=https://<worker-url>| Command | Description |
|---|---|
pnpm dev |
Start app locally (runs theme generation first) |
pnpm build |
Type-check and build production bundle |
pnpm preview |
Preview production build locally |
pnpm test |
Run unit/integration tests |
pnpm test:ui |
Run Vitest in UI mode |
pnpm lint |
Run ESLint |
pnpm lint:fix |
Auto-fix lint issues |
pnpm format |
Format source files with Prettier |
pnpm format:check |
Check formatting without writing |
pnpm generate:themes |
Rebuild src/data/themes.generated.ts from src/themes/* |
pnpm storybook |
Run Storybook on port 6006 |
pnpm build-storybook |
Build static Storybook site |
src/
components/ UI pages and reusable components
documents/ Documents listing and cards
templates/ Template listing and cards
themes/ Theme gallery and theme cards
theme-editor/ Custom theme creation UI
preview/ Markdown preview renderer
data/
themes.generated.ts Auto-generated theme presets and helpers
systemTemplates.ts Built-in template seeds
lib/
repositories/ Data layer interfaces + IndexedDB implementations
markdown-templating/ Frontmatter + Nunjucks processing
themes/ Source theme folders (config.json + example.md)
scripts/
generateThemes.js Builds src/data/themes.generated.ts
Themes are defined under src/themes/<theme-id>/ with:
config.json(metadata + Tailwind classes)example.md(sample content shown in previews)
Running:
pnpm generate:themesgenerates src/data/themes.generated.ts, which exports:
themePresetsThemeCategoryandTHEME_CATEGORIES- helper functions such as
getThemeById,filterThemesByCategory,searchThemes
- Create
src/themes/<your-theme-id>/config.json - Add
src/themes/<your-theme-id>/example.md - Run
pnpm generate:themes - Open
/themesor Storybook to validate visuals
Templates can include frontmatter defaults and placeholders:
---
title: My Report
author: Jane Doe
company:
name: Acme Corp
---
# {{title}}
By **{{author}}**
Company: {{company.name}}You can also use Nunjucks control flow:
{% if condition %} ... {% endif %}{% for item in items %} ... {% endfor %}
Published pages are rendered as read-only views where document content is encoded in the URL (/s/:pako route). This enables easy sharing without user accounts or server-side document storage.
Issues and pull requests are welcome.
- Create a branch from
main - Implement your change with tests when relevant
- Run lint/tests/build locally
- Open a PR using Conventional Commits