Releases: charmbracelet/huh
v2.0.3
This patch release fixes two UX issues where multiline options might get cutoff when navigating, and the other is related to ensuring the viewport width is updated after a resize.
Changelog
Fixed
- 3c0116c: fix(select): ensure cursor visibility when navigating multiline options (#749) (@aymanbagabas)
- ec53694: fix(select): recompute viewport width on WithWidth (#747) (@SergioChan)
Thoughts? Questions? We love hearing from you. Feel free to reach out on X, Discord, Slack, The Fediverse, Bluesky.
v2.0.2
Paste Paste Paste
This patch release fixes an issue where sometimes, a paste event, might get triggered multiple times. Enjoy!
Changelog
Fixed
- 8ccc9e1: fix: Prevent double paste in focused field (#746) (@ConnorsApps)
- 35e08d4: fix: use keypress events instead of key events to avoid release events (@aymanbagabas)
Docs
- 28dc68f: docs(license): update copyright date (@meowgorithm)
- 4b802ae: docs(readme): update footer (@meowgorithm)
Thoughts? Questions? We love hearing from you. Feel free to reach out on X, Discord, Slack, The Fediverse, Bluesky.
v2.0.1
v2.0.0
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 • نحنُ نحب المصادر المفتوحة
v0.8.0
Fields aren’t just for farmers
Hi! This big feature in this release is that you can now create and maintain your own Field types as Field.RunAccessible is now public. Check out the Field interface and go crazy.
We also fixed a handful of bugs in the release. Special thanks to @pashpashpash for nudging us to cut a release.
Changelog
New!
Fixed
- fix(select): do not show title if filter is set by @caarlos0 in #633
- fix(select): make sure selected item is visible on viewport update by @caarlos0 in #632
- fix(select,multiselect): properly handle defaults and prompts by @caarlos0 in #642
- fix: Terminal.app rendering issues by @caarlos0 in #643
- fix: minimize the risk of scrolling the view up on some terminals by @aymanbagabas in #649
- fix: Add missing id initialization in field_select constructor by @forever-salty in #692
Other Stuff
- docs(readme): various small updates by @meowgorithm in #651
- chore(deps): use bubbles@master by @caarlos0 in #670
- docs(filepicker): correct godoc comment for DirAllowed function by @mohammedyh in #681
New Contributors
- @mohammedyh made their first contribution in #681
- @forever-salty made their first contribution in #692
Full Changelog: v0.7.0...v0.8.0
v0.7.0
Less bugs, more feats
This is a quality-of-life release which fixes a handful of behavioral and rendering issues, and adds a few of useful features.
go get github.com/charmbracelet/huh@latestAccessible mode: now more accessible
We made several updates to accessible mode, and it should now work better with
screen readers.
It will also now respect WithInput and WithOutput.
Focused? Hovered Filtered?
Useful when integrating with an existing Bubble Tea app, you can now get the
currently focused field, as well as the which option the cursor is pointing at, which we’re calling "hover":
f := huh.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Options(huh.NewOptions(
"Banana",
"Apple",
"Orange",
)...).
Title("Favorite fruit?"),
huh.NewMultiSelect[string]().
Options(huh.NewOptions(
"Pudim",
"Sagu",
"Chocolate",
)...).
Title("Favorite dessert?"),
),
)
_ = f.Run()
field := f.GetFocusedField()
switch field := field.(type) {
case *huh.Select[string]:
fmt.Println(field.Hovered())
case *huh.MultiSelect[string]:
fmt.Println(field.Hovered())
}You can also use GetFiltering to check if the user is currently filtering.
Spinning, but not out of control
Spinner was revamped and now properly handles context cancellations,
interrupts, and more. You can also use the new ActionWithError to set an action that might error.
Other improvements
FilePickergot a couple of improvements: you can now set theCursor, and the UI has received a bit of extra polish.Groupnow properly renders theirTitleandDescription.Textcan now be configured to not allow opening the external editor.SelectandMultiSelectnow properly handle multi-line options, as well as automatically wrap long options so they are properly rendered.TitleandDescriptionof all components now properly wrap, fixing many rendering issues.- Both
ColumnandGridlayouts received bug fixes and improvements. - Interrupt signals (
SIGINT) are now properly handled.
Changelog
- feat(accessibility): prompt improvements by @caarlos0 in #620
- feat(input): Skip validation when going to previous field by @Sculas in #285
- feat(spinner): improve context, action, output, tests by @caarlos0 in #292
- feat: Add Cursor() methods to Select and MultiSelect fields by @kralicky in #407
- feat: add
WithButtonAlignmentfunction for button positioning by @bashbunni in #427 - feat: add cursor option to filepicker by @radar07 in #400
- feat: expose focused field by @jonas-grgt in #503
- feat: group titles and descriptions by @caarlos0 in #548
- feat: method to check filtering state of select fields by @jon4hz in #524
- feat: option to disable external editor in Text field by @rahji in #516
- fix rendering of dynamic fields in groups by @zhammer in #505
- fix(accessibility): parseBool defaults to no by @jaredallard in #442
- fix(accessibility): stop scanning on EOF from stdin by @aybabtme in #440
- fix(filepicker): auto-height by @caarlos0 in #464
- fix(filepicker): height, navigation by @caarlos0 in #593
- fix(filepicker): set padding and width to avoid cluttering by @caarlos0 in #465
- fix(select,multiselect): multi line items, scrolling by @caarlos0 in #569
- fix: accessible mode IO, default values, and more by @caarlos0 in #614
- fix: form and group styles by @caarlos0 in #567
- fix: height and width calculation improvements, wrapping by @caarlos0 in #573
- fix: ignore next input bug on Windows by @awoodbeck in #520
- fix: improve timeout by @caarlos0 in #600
- fix: properly handle interrupts by @caarlos0 in #491
- fix: stop using viewport deprecated methods by @caarlos0 in #621
- fix: wrap titles and descriptions when needed by @caarlos0 in #570
- Fixed README GetString to GetInt for bubbletea form example by @hegner123 in #574
- Fixed comment to match function by @hegner123 in #580
- docs(examples): fix typo by @bashbunni in #506
- docs(examples): various small corrections by @meowgorithm in #602
- docs: add example of Dynamic Huh? inside Bubble Tea application by @maaslalani in #347
- Update README.md by @chastain in #575
- chore: basic repo files maintenance by @andreynering in #616
- chore: update github templates by @bashbunni in #603
New Contributors
- @radar07 made their first contribution in #400
- @aybabtme made their first contribution in #440
- @jaredallard made their first contribution in #442
- @awoodbeck made their first contribution in #520
- @jon4hz made their first contribution in #524
- @charmcli made their first contribution in #535
- @jonas-grgt made their first contribution in #503
- @rahji made their first contribution in #516
- @zhammer made their first contribution in #505
- @kralicky made their first contribution in #407
- @hegner123 made their first contribution in #574
- @chastain made their first contribution in #575
- @github-actions made their first contribution in #608
- @andreynering made their first contribution in #616
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Discord.
v0.6.0
Just focus
This release features automatic window focus events support for the Input and Text components.
Note that if you're using Huh in a larger Bubble Tea program you’ll need WithReportFocus to enable focus events.
p := tea.NewProgram(model{}, tea.WithReportFocus())Happy focusing!
Changelog
New!
Full Changelog: v0.5.3...v0.6.0
v0.5.3
Crushin’ bugs
This release fixes a buncha bugs in Huh and Gum alike. Gum users, stay tuned for an update later today.
What's Changed
Fixed
- fix: group varying heights by @bashbunni in #350
- fix: group size when height is 0 by @caarlos0 in #363
- fix(filepicker): not closing on close by @caarlos0 in #323
- fix(select): wrapping selects by @caarlos0 in #324
- fix(select): select all/none by @caarlos0 in #325
- fix(multiselect): contextual 'select all' help text by @meowgorithm in #342
- fix(spinner): light color in title by @hyorigo in #346
- fix (multiselect): set filterable not working by @caarlos0 in #335
- fix(lint): golangci-lint issues by @caarlos0 in #378
Other Stuff
- chore: update codeowners by @caarlos0 in #379
- refactor: use a selector type to select items by @aymanbagabas in #328
- refactor: make item selector into a slice container by @aymanbagabas in #334
New Contributors
- @bashbunni made their first contribution in #350
- @hyorigo made their first contribution in #346
Full Changelog: v0.5.2...v0.5.3
v0.5.2
Lil’ fixes ’n’ improvements
Hi! This is a maintenance release to fix issues with dynamic forms as well as address issues with Gum upstream.
Changelog
New
- add accept and reject key bindings to confirm by @csandeep in #308
- allow Generic
TupdateOptionsMsg by @maaslalani in #318
Fixed
- improve distinction in field select by @csandeep in #304
- fix invalid order of event handling in input/text fields by @Sculas in #284
New Contributors
- @csandeep made their first contribution in #304
- @MaximilianSoerenPollak made their first contribution in #313
Full Changelog: v0.5.1...v0.5.2
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Discord.
v0.5.0
This big news in this release is that forms in Huh can now be dynamic. Read on for more:
Dynamic Forms 🪄
huh? forms can now react to changes in other parts of the form. Replace properties such as Options, Title, Description with their dynamic counterparts: OptionsFunc, TitleFunc, and DescriptionFunc to recompute properties values on changes when watched variables change.
Let’s build a simple state / province picker.
var country string
var state stringThe country select will be static, we’ll use this value to recompute the
options and title for the next input.
huh.NewSelect[string]().
Options(huh.NewOptions("United States", "Canada", "Mexico")...).
Value(&country).
Title("Country").Define your Select with TitleFunc and OptionsFunc and bind them to the
&country value from the previous field. Whenever the user chooses a different
country, the TitleFunc and OptionsFunc will be recomputed.
Important
We have to pass &country as the binding to recompute the function only when
country changes, otherwise we will hit the API too often.
huh.NewSelect[string]().
Value(&state).
Height(8).
TitleFunc(func() string {
switch country {
case "United States":
return "State"
case "Canada":
return "Province"
default:
return "Territory"
}
}, &country).
OptionsFunc(func() []huh.Option[string] {
opts := fetchStatesForCountry(country)
return huh.NewOptions(opts...)
}, &country),Lastly, run the form with these inputs.
err := form.Run()
if err != nil {
log.Fatal(err)
}Changelog
New!
- Form Layouts by @adamdottv in #274
CursorTextstyle by @nervo in #259WithInputby @Delta456 in #271WithTimeoutby @Delta456 in #276- Introduce
accessorby @nervo in #263 - Introduce
accessorby @nervo in #263 - Set filtering state of select on init by @PJGaetan in #179
Fixed
- Aborting a form returned a timeout error by @Sculas in #287
- Resolve conflict between select and filter by @MikaelFangel in #252
- Adjust input width to char limit by @nervo in #260
New Contributors
- @MikaelFangel made their first contribution in #252
- @nervo made their first contribution in #253
- @shedyfreak made their first contribution in #230
- @Delta456 made their first contribution in #271
- @abradley2 made their first contribution in #280
- @PJGaetan made their first contribution in #179
- @Sculas made their first contribution in #287
Full Changelog: v0.4.2...v0.5.0
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Discord.


