Skip to content
Integrating Chroma syntax highlighter as a Blackfriday renderer
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
example Fixing dependencies Jan 5, 2018
.drone.yml Improving module Jun 11, 2019
Gopkg.lock Adding go1.11 module support, and updating documentation Jul 23, 2018
LICENSE Initial commit Oct 9, 2017 Adding badges because I love badges Oct 12, 2018
go.mod Improving module Jun 11, 2019
go.sum Improving module Jun 11, 2019
renderer_test.go Fixing tests Jan 5, 2018



Go Version Go Version Go Report Card Build Status codecov License Godoc Sourcegraph Say Thanks!

Integrating Chroma syntax highlighter as a Blackfriday renderer.

Install and prerequisites

This project requires and uses the v2 version of Blackfriday. After this issue I decided to rollback to the version so the lib can be go gettable.

$ go get -u

You can also install it directly by using dep in your project:

$ dep ensure -add

Additionnaly, this project uses the module approach of go 1.11


This renderer integrates chroma to highlight code with triple backtick notation. It will try to use the given language when available otherwise it will try to detect the language. If none of these two method works it will fallback to sane defaults.


bfchroma uses the functional options approach so you can customize the behavior of the renderer. It uses sane defaults when no option is passed so you can use the renderer simply by doing so :

html := bf.Run([]byte(md), bf.WithRenderer(bfchroma.NewRenderer()))


  • Style(s string)
    Define the style used by chroma for the rendering. The full list can be found here
  • ChromaStyle(*chroma.Style)
    This option can be used to passe directly a *chroma.Style instead of the string representing the style as with the Style(string) option.
  • WithoutAutodetect()
    By default when no language information is written in the code block, this renderer will try to auto-detect the used language. This option disables this behavior and will fallback to a sane default when no language information is available.
  • Extend(bf.Renderer)
    This option allows to define the base blackfriday that will be extended.
  • ChromaOptions(...html.Option)
    This option allows you to pass Chroma's html options in the renderer. Such options can be found here. There is currently an issue with the html.WithClasses() option as it expects the CSS classes to be written separately. I'll come up with a fix later.

Option examples

Disabling language auto-detection and displaying line numbers

r := bfchroma.NewRenderer(

Extend a blackfriday renderer

b := bf.NewHTMLRenderer(bf.HTMLRendererParameters{
	Flags: bf.CommonHTMLFlags,

r := bfchroma.NewRenderer(bfchroma.Extend(b))

Use a different style

r := bfchroma.NewRenderer(bfchroma.Style("dracula"))
// Or
r = bfchroma.NewRenderer(bfchroma.ChromaStyle(styles.Dracula))


package main

import (


	bf ""

var md = "This is some sample code.\n\n```go\n" +
	`func main() {
` + "```"

func main() {
	html := bf.Run([]byte(md), bf.WithRenderer(bfchroma.NewRenderer()))

Will output :

<p>This is some sample code.</p>
<pre style="color:#f8f8f2;background-color:#272822"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Hi&#34;</span>)

Real-life example

In smallblog I'm using bfchroma to render my articles. It's using a combination of both bfchroma's options and blackfriday extensions and flags.

package main

import (
	bf ""

// Defines the extensions that are used
var exts = bf.NoIntraEmphasis | bf.Tables | bf.FencedCode | bf.Autolink |
	bf.Strikethrough | bf.SpaceHeadings | bf.BackslashLineBreak |
	bf.DefinitionLists | bf.Footnotes

// Defines the HTML rendering flags that are used
var flags = bf.UseXHTML | bf.Smartypants | bf.SmartypantsFractions |
	bf.SmartypantsDashes | bf.SmartypantsLatexDashes | bf.TOC

// render will take a []byte input and will render it using a new renderer each
// time because reusing the same can mess with TOC and header IDs
func render(input []byte) []byte {
	return bf.Run(
						Flags: flags,


If you have loads of code in your markdown, you might want to consider using html.WithClasses() in your bfchroma.ChromaOptions(). The CSS of the style you chose can then be accessed like this :

r := bfchroma.NewRenderer(
		bf.NewHTMLRenderer(bf.HTMLRendererParameters{Flags: flags}),

var css template.CSS

b := new(bytes.Buffer)
if err := r.Formatter.WriteCSS(b, r.Style); err != nil {
	logrus.WithError(err).Warning("Couldn't write CSS")
css = template.CSS(b.String())

bf.Run(input, bf.WithRenderer(r), bf.WithExtensions(exts))

This way, you can pass your css var to any template and render it along the rendered markdown.

You can’t perform that action at this time.