benmark turns Markdown into safe, styled HTML in one Go import. It is a
batteries-included distribution built on goldmark:
CommonMark + GitHub-Flavored Markdown, plus syntax highlighting, Mermaid
diagrams, media embeds (video / audio / PDF / YouTube / Vimeo), heading
anchors, wikilinks, and front matter — sanitized by default so the output
is safe to drop straight into innerHTML.
Think of it as pip install markdown for Go, except it also ships the CSS and
the client-side hydration for diagrams and embeds.
import "github.com/benmore-studio/benmark"
html, err := benmark.RenderString("# Hello\n\nSome **markdown** with a ```mermaid``` diagram.")Most Go Markdown libraries give you a parser and stop there — you still wire up highlighting, sanitization, a theme, and client rendering yourself, and every project re-derives the same glue. benmark is the glue, done once and safely:
- ✅ Correct — CommonMark + GFM via goldmark (the parser Hugo uses).
- ✅ Rich — tables, task lists, footnotes, definition lists, strikethrough,
autolinks, wikilinks, front matter, and
:emoji:shortcodes. - ✅ Callouts — GitHub-style
> [!NOTE]/[!TIP]/[!WARNING]/[!IMPORTANT]/[!CAUTION]alerts, styled by the theme. - ✅ Diagrams —
```mermaidfences become hydration hooks rendered by mermaid.js on the client. - ✅ Highlighted — fenced code is syntax-highlighted with
chroma (class-based; ship
CSS()), with a copy button added byHydrateJS(). - ✅ Media — images (incl. inline
data:images) and bare URLs that point at.mp4/.mp3/.pdf, or at YouTube/Vimeo, become<video>/<audio>/<iframe>embeds. - ✅ Safe by default — output is sanitized with
bluemonday; scripts, inline
event handlers, and
javascript:/data:URLs never survive. - ✅ Styled —
CSS()returns a self-contained, light/dark-aware stylesheet. - ✅ Batteries —
HydrateJS()renders diagrams client-side after insertion.
go get github.com/benmore-studio/benmarkhtml, err := benmark.RenderString(src) // sanitized HTML, default configr := benmark.New(
benmark.WithHighlightTheme("github"),
benmark.WithMediaEmbeds(true),
)
html, err := r.Render([]byte(src))doc, _ := benmark.New().RenderDoc([]byte("---\ntitle: Hi\n---\n# Body"))
doc.HTML // rendered body (front matter stripped)
doc.Meta["title"] // "Hi"mux.HandleFunc("GET /markdown.css", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/css")
io.WriteString(w, benmark.CSS())
})
mux.HandleFunc("GET /markdown-hydrate.js", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/javascript")
io.WriteString(w, benmark.HydrateJS())
})Wrap the rendered HTML in class="benmark", include markdown.css, load
mermaid.js, and call hydrate(el) after inserting the HTML.
// One-line messages stay inline (no wrapping <p>, newlines become <br>).
html, _ := benmark.RenderString(msg, benmark.WithInline(true))| Option | Default | Effect |
|---|---|---|
WithSanitize(bool) |
on | bluemonday allowlist over the output |
WithHighlightTheme(string) |
"github" |
chroma style; "" disables |
WithHeadingAnchors(bool) |
on | heading ids + ¶ anchors |
WithMermaid(bool) |
on | ```mermaid → <pre class="mermaid"> |
WithMediaEmbeds(bool) |
on | video / audio / PDF / YouTube / Vimeo |
WithWikilinks(bool) |
on | [[Page]] links |
WithEmoji(bool) |
on | :tada: → 🎉 shortcodes |
WithFrontmatter(bool) |
on | parse + strip YAML/TOML front matter |
WithInline(bool) |
off | single-line "chat" subset |
WithEmbedHosts(...string) |
YouTube+Vimeo | iframe host allowlist |
Output is sanitized by default and is safe for innerHTML. <script>, inline
event handlers (onerror, onclick, …), and dangerous URL schemes
(javascript:, data:, vbscript:) are removed. <iframe> is pinned to
YouTube/Vimeo player URLs and PDF documents. Only disable sanitization
(WithSanitize(false)) for first-party Markdown you fully trust.
MIT — see LICENSE. benmark builds on goldmark, chroma, bluemonday, and the goldmark extensions by Abhinav Gupta; see NOTICE.