Huh? v2?
We're thrilled to announce the second major release of Huh!
Note
We take API changes seriously, and we want to make the upgrade process as simple as possible.
❤️ Charm Land Import Path
We've updated our import paths to use vanity domains and use our domain to import Go packages.
// Before
import "github.com/charmbracelet/huh"
// After
import "charm.land/huh/v2"🍵 Bubble Tea v2 & Lip Gloss v2
Huh v2 is built on the all-new Bubble Tea v2 and Lip Gloss v2, bringing all their improvements along for the ride:
- The Cursed Renderer — optimized rendering built from the ground up on the ncurses algorithm
- Better keyboard handling — progressive keyboard enhancements for modern terminals
- Declarative views — no more fighting over terminal state
- Built-in color downsampling — colors "just work" everywhere
- And much more — see the Bubble Tea v2 release notes for the full picture
All these improvements come for free. Just upgrade and enjoy the performance and stability benefits.
🎨 Simpler Theming
Themes are now passed by value instead of pointer. This makes theme handling more straightforward and predictable.
// Before
form.WithTheme(huh.ThemeCharm())
// After
form.WithTheme(huh.ThemeCharm(false)) // false for light modeAll built-in themes now take a bool parameter to indicate whether the terminal has a dark background. Huh will automatically detect your terminal's background color, but you can also provide your own custom theme function:
type ThemeFunc func(isDark bool) *Styles
form.WithTheme(myCustomTheme)🔍 View Hooks
Want to modify your form's view before it hits the screen? Now you can with WithViewHook:
form.WithViewHook(func(v tea.View) tea.View {
// Modify the view properties
v.AltScreen = true
v.MouseMode = tea.MouseModeAllMotion
return v
})This is perfect for dynamically controlling terminal features, applying custom view transformations, or integrating Huh forms with larger Bubble Tea applications.
♿ Simplified Accessible Mode
The separate accessibility package is gone. Accessible mode is now built directly into Huh and controlled exclusively at the form level:
// Before - individual fields had their own accessible mode
input := huh.NewInput().
Title("Name").
WithAccessible(true) // ❌ No longer exists on fields
select := huh.NewSelect[string]().
Title("Pick one").
Options(huh.NewOptions("A", "B", "C")...).
WithAccessible(true) // ❌ Removed from fields too
form := huh.NewForm(
huh.NewGroup(input, select),
)
// After - only the form controls accessible mode
input := huh.NewInput().
Title("Name")
select := huh.NewSelect[string]().
Title("Pick one").
Options(huh.NewOptions("A", "B", "C")...)
form := huh.NewForm(
huh.NewGroup(input, select),
).WithAccessible(true) // ✅ Set once at the form levelThis makes accessible mode simpler and more consistent — one setting controls the entire form.
Tip
We recommend detecting accessible mode through environment variables or configuration options to let users control accessibility on their terms.
🗂️ Better Model Handling
The internal Model type is now exposed, making it easier to work with Huh forms in Bubble Tea applications. This improves type safety and makes composition patterns more natural.
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
form, cmd := m.form.Update(msg)
if f, ok := form.(huh.Model); ok {
m.form = &f
}
return m, cmd
}🧹 Cleaner Dependencies
Huh v2 benefits from the simplified dependency tree of Bubble Tea v2 and Lip Gloss v2. This means faster builds, smaller binaries, and fewer potential version conflicts.
🌈 More on Huh v2
Ready to migrate? Head over to the Upgrade Guide for the full migration checklist.
Changelog
New!
- 523aee7: feat(ci): use goreleaser for releases (@aymanbagabas)
- 3e45cdc: feat: allow to set a view hook (@caarlos0)
Fixed
- df2a688: fix(test): remove unstyled whitespace from tests (@bashbunni)
- 76b6bb3: fix: bug (@caarlos0)
- 7968fed: fix: clamp (@caarlos0)
- fc6309d: fix: examples, tests (@caarlos0)
- 8102372: fix: lint issues (@caarlos0)
- 2e53e04: fix: reader (@caarlos0)
- a71c3ac: fix: remove old accessible pkg (@caarlos0)
- e50afa6: fix: rename to match (@caarlos0)
- 555b977: fix: spinner accessiblity (@caarlos0)
- b882a1c: fix: spinner output (@caarlos0)
- 042dfb8: fix: tests (@caarlos0)
- 2785463: fix: todos (@caarlos0)
- 12c1596: fix: update glamour, log, wish (@caarlos0)
- 61e9d3f: fix: update to charm.land (@caarlos0)
- c698df8: fix: use lipgloss for ansi aware wrapping instead of cellbuf (@aymanbagabas)
Docs
- 9a59654: docs: add v2 upgrade guide (@aymanbagabas)
Other stuff
- fc0c3f0: ci: build (@caarlos0)
- 8c33304: ci: build fix (@caarlos0)
- 6a0c589: ci: fix build (@caarlos0)
- b2ec783: feat!(v2): spinner does not need to be its own module (@caarlos0)
- ec05bab: feat!: v2 (@caarlos0)
- 6421b74: wip (@caarlos0)
- 548eedb: wip (@caarlos0)
- 47e59e9: wip (@caarlos0)
- 1d4dd36: wip: v2 (@caarlos0)
Feedback
Have thoughts on Huh v2? We'd love to hear about it. Let us know on…
Part of Charm.
Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة