This guide will help you migrate from Glamour v1 to v2. Most upgrades are straightforward and can be completed in minutes.
Required: All imports must use the new charm.land module path with /v2:
-import "github.com/charmbracelet/glamour"
-import "github.com/charmbracelet/glamour/ansi"
-import "github.com/charmbracelet/glamour/styles"
+import "charm.land/glamour/v2"
+import "charm.land/glamour/v2/ansi"
+import "charm.land/glamour/v2/styles"go get charm.land/glamour/v2@latestIf you need color downsampling (most apps do):
go get charm.land/lipgloss/v2@latestBreaking: WithAutoStyle() has been removed. The default style is now "dark".
-r, _ := glamour.NewTermRenderer(
- glamour.WithAutoStyle(),
-)
+r, _ := glamour.NewTermRenderer(
+ // "dark" is the default, or specify explicitly
+ glamour.WithStylePath("dark"),
+)If you were relying on automatic style selection based on terminal background, you'll need to choose the style explicitly:
// For light backgrounds
r, _ := glamour.NewTermRenderer(glamour.WithStylePath("light"))
// For dark backgrounds (default)
r, _ := glamour.NewTermRenderer(glamour.WithStylePath("dark"))
// Other built-in styles: "pink", "dracula", "tokyo-night", "ascii"
r, _ := glamour.NewTermRenderer(glamour.WithStylePath("dracula"))Want to detect the terminal background yourself? You can use Lip Gloss:
import "charm.land/lipgloss/v2"
// Detect if we're on a dark background
isDark := lipgloss.HasDarkBackground()
style := "dark"
if !isDark {
style = "light"
}
r, _ := glamour.NewTermRenderer(glamour.WithStylePath(style))Breaking: WithColorProfile() has been removed. Use Lip Gloss for color adaptation:
-import "github.com/muesli/termenv"
-
-r, _ := glamour.NewTermRenderer(
- glamour.WithColorProfile(termenv.TrueColor),
-)
-out, _ := r.Render(markdown)
-fmt.Print(out)
+import "charm.land/lipgloss/v2"
+
+r, _ := glamour.NewTermRenderer(
+ glamour.WithWordWrap(80),
+)
+out, _ := r.Render(markdown)
+
+// Lip Gloss handles color downsampling based on terminal capabilities
+lipgloss.Print(out)Why the change? Glamour is now pure β it always produces the same output for the same input. This makes it more predictable and testable. Lip Gloss handles the terminal-specific color adaptation when you're ready to display the output.
If you don't need color adaptation (e.g., you know you're always outputting TrueColor):
r, _ := glamour.NewTermRenderer(glamour.WithWordWrap(80))
out, _ := r.Render(markdown)
fmt.Print(out) // Direct output, no downsamplingBreaking: The Overlined field has been removed from style configurations.
If you have custom styles using Overlined:
StylePrimitive: ansi.StylePrimitive{
Bold: &trueBool,
Underline: &trueBool,
- Overlined: &trueBool,
}Overline was rarely supported across terminals and not widely used. If you need similar visual separation, consider alternatives like underline, bold, inverse, or background colors.
If you maintain custom StyleConfig definitions, update the import paths:
-import "github.com/charmbracelet/glamour/ansi"
+import "charm.land/glamour/v2/ansi"
var myStyle = &ansi.StyleConfig{
// Your custom style definition
Document: ansi.StyleBlock{
StylePrimitive: ansi.StylePrimitive{
Color: stringPtr("#E6DB74"),
},
},
}The structure is the same; only the import path changes.
If you implemented custom margin or padding writers using ansi.MarginWriter:
- Ensure you call
.Close()on all writer instances - The new
IndentWriterandPaddingWritertypes are available for custom use
import "charm.land/glamour/v2/ansi"
mw := ansi.NewMarginWriter(ctx, w, style)
defer mw.Close() // Important: always close writers now
// Write your content
io.WriteString(mw, content)This improves memory management and prevents resource leaks.
Here's a complete before/after example:
package main
import (
"fmt"
"github.com/charmbracelet/glamour"
"github.com/muesli/termenv"
)
func main() {
md := `# Hello World
This is **Glamour v1**!
`
r, _ := glamour.NewTermRenderer(
glamour.WithAutoStyle(),
glamour.WithColorProfile(termenv.TrueColor),
glamour.WithWordWrap(80),
)
out, _ := r.Render(md)
fmt.Print(out)
}package main
import (
"fmt"
"charm.land/glamour/v2"
"charm.land/lipgloss/v2"
)
func main() {
md := `# Hello World
This is **Glamour v2**!
`
r, _ := glamour.NewTermRenderer(
glamour.WithStylePath("dark"), // or omit for default
glamour.WithWordWrap(80),
)
out, _ := r.Render(md)
lipgloss.Print(out) // Handles color downsampling
}After making changes:
- Run your tests β Ensure all tests pass with the new version
- Visual check β Render sample markdown and verify output looks correct
- Check wrapping β Pay special attention to text wrapping if you have CJK or emoji content (it should be better now!)
- Test hyperlinks β If you use autolinks, verify they render correctly
Make sure you've updated your go.mod:
go get charm.land/glamour/v2And that all imports use the new path with /v2.
If colors aren't displaying correctly, make sure you're using lipgloss.Print() instead of fmt.Print():
out, _ := r.Render(markdown)
lipgloss.Print(out) // Not fmt.Print(out)Glamour v2 has improved text wrapping, especially for CJK characters and emojis. If you're seeing wrapping issues, it's likely a regression. Please open an issue!
If you run into issues during migration:
- Check the examples directory for working code
- Review What's New for detailed feature changes
- Join us on Discord
- Open an issue on GitHub
Migrated successfully? Having trouble? We'd love to hear about it!
Welcome to Glamour v2! π
Part of Charm.
Charmηη±εΌζΊ β’ Charm loves open source
