A Slack app that renders Markdown files shared in Slack messages as rich, styled HTML pages with syntax highlighting, Mermaid diagrams, and math formula support.
- GitHub-Flavored Markdown — tables, task lists, strikethrough, headings, lists, and more
- Syntax Highlighting — code blocks highlighted by highlight.js with language auto-detection
- Mermaid Diagrams — render flowcharts, sequence diagrams, Gantt charts, and more from
```mermaidfenced code blocks - KaTeX Math — inline (
$...$) and display ($$...$$) math expressions rendered by KaTeX - Smart Typography — curly quotes, em-dashes, and ellipses via
marked-smartypants - Light/Dark Mode — automatic theme switching based on system preference
- Links open in new tab — all links get
target="_blank"andrel="noopener noreferrer" - Lazy-loaded images — images use
loading="lazy"for performance - Modal popups — results and errors are shown in modals, not ephemeral messages
- OAuth flow — users install the app to grant
files:readscope for downloading file contents
User clicks "Render Markdown" on a message with a .md file
│
▼
┌──────────────────────────────────┐
│ Single Deno HTTP server │ main.ts
│ │
│ 1. Receive Slack shortcut │
│ (message action payload) │
│ 2. Look up user OAuth token │
│ from Deno KV │
│ 3. Extract .md files from │
│ payload.message.files │
│ (no conversations.history │
│ needed — works anywhere) │
│ 4. Download file content via │
│ files.info + CDN (or preview │
│ text fallback) │
│ 5. Render Markdown → HTML via │
│ marked; convert preview to │
│ Slack mrkdwn │
│ 6. Store raw Markdown in Deno KV │
│ (1h TTL; auto-chunked into │
│ 60KB pieces for large files) │
│ 7. Open Slack modal with │
│ preview and "Open rendered" │
│ link │
└────────┬─────────────────────────┘
│ /render/{id}
▼
Re-render Markdown → HTML on demand
│
▼
serve styled page (highlight.js,
Mermaid, KaTeX)
The entire app is a single Deno HTTP server that handles:
- Slack Events — interactive component payloads (message shortcuts)
- OAuth — install flow with
oauth.v2.access - Rendering — Markdown → HTML conversion, on-demand for every page load
- Serving — renders Markdown with a styled template (highlight.js, Mermaid, KaTeX)
- Deno 1.40+
- A Slack workspace where you can install apps
- A Slack app with Interactivity, OAuth, and the
files:readuser scope enabled
git clone https://github.com/your-username/slack-render-md.git
cd slack-render-mdThe project uses Deno, which resolves dependencies at runtime. No install step is needed.
cp .env.example .env| Variable | Description |
|---|---|
SLACK_BOT_TOKEN |
Slack bot token (xoxb-...) |
SLACK_CLIENT_ID |
Slack app client ID |
SLACK_CLIENT_SECRET |
Slack app client secret |
RENDERER_BASE |
Public base URL (defaults to https://slack-render-md.aliveonline.deno.net) |
deno task startThis starts the server at http://localhost:8080.
For local development with Slack, use ngrok or a similar tunnel to expose your local server, then configure the Slack app's Request URL to point to your tunnel.
In the Slack API dashboard:
- Create a new app (or use an existing one)
- Enable Interactivity & Shortcuts → add a Message Shortcut with callback ID
render_md_file - Add OAuth & Permissions — set Redirect URL to
https://your-domain/slack/oauth_redirect - Add bot scopes:
chat:write,channels:history,groups:history,im:history,mpim:history - Add user scope:
files:read - Copy the Bot Token, Client ID, and Client Secret to your
.env
deno task deployOr push to main — the GitHub Actions workflow deploys automatically.
- In Slack, find any message that contains a Markdown file (
.mdor.markdownextension). - Hover over the message, open the More actions menu (three dots), and select Render Markdown file.
- If you haven't installed the app, you'll be prompted to install (grants
files:readfor downloading files). - If multiple
.mdfiles are attached, a modal picker shows each file with a Render button — click one to render that file. - The app opens a modal with a plain-text preview and a link to open the full rendered Markdown.
- Click the link to view the rendered HTML page with syntax highlighting, diagrams, and math.
- The app does not need to be invited to channels or DMs — it works from any message action menu.
├── .github/workflows/
│ └── deploy.yml # Deploy to Deno Deploy
├── templates/
│ └── render.html # HTML template with highlight.js, Mermaid, KaTeX
├── main.ts # Single HTTP server (Slack events, OAuth, rendering)
├── main_test.ts # Tests for KV storage
├── renderer.ts # Markdown → HTML + HTML → Slack mrkdwn
├── renderer_test.ts # Tests for renderer
├── .env.example # Environment variable template
├── deno.jsonc # Deno configuration, tasks, dependencies
└── AGENTS.md # AI assistance guidelines
deno task testThis runs deno fmt --check, deno lint, and the test suite. Tests cover:
| Module | Tests |
|---|---|
renderMarkdown |
Basic formatting, code blocks, tables, mermaid, links, lists, task lists, strikethrough, images, blockquotes, HR, headings |
htmlToSlack |
Bold, italic, strikethrough, code, links, headings, paragraphs, line breaks, lists, HR, blockquotes, HTML entities, whitespace collapse, length limit, empty input |
| KV storage | Inline content, chunked content (over 60KB), empty string, boundary values, unicode, missing key |
chat:write— send messages (required for fallback ephemeral replies)files:read(user scope) — download file contents via CDN
channels:history,groups:history,im:history, andmpim:historyare not required for the primary flow — the app reads.mdfiles directly from the message action payload. They're only needed as a fallback if Slack changes the payload format.
| Component | Technology |
|---|---|
| Runtime | Deno |
| Markdown Parser | marked 15.x |
| Smart Typography | marked-smartypants |
| Syntax Highlighting | highlight.js 11.x |
| Diagrams | Mermaid 10.x |
| Math | KaTeX 0.16.x |
| Markdown CSS | github-markdown-css |
| Persistence | Deno KV |
| Hosting | Deno Deploy |