Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
H

huh

> 编程语言
Open source

Build terminal forms and prompts ‍♀️

7.1K stars0 likes0 views
WebsiteGitHub

About

Build terminal forms and prompts ‍♀️

# Huh?



A simple, powerful library for building interactive forms and prompts in the terminal. `huh?` is easy to use in a standalone fashion, can be [integrated into a Bubble Tea application](#what-about-bubble-tea), and contains a first-class [accessible mode](#accessibility) for screen readers. The above example is running from a single Go program ([source](./examples/burger/main.go)). ## Tutorial Let’s build a form for ordering burgers. To start, we’ll import the library and define a few variables where we'll store answers. ```go package main import "charm.land/huh/v2" var ( burger string toppings []string sauceLevel int name string instructions string discount bool ) ``` `huh?` separates forms into groups (you can think of groups as pages). Groups are made of fields (e.g. `Select`, `Input`, `Text`). We will set up three groups for the customer to fill out. ``` … ``` Finally, run the form: ```go err := form.Run() if err != nil { log.Fatal(err) } if !discount { fmt.Println("What? You didn’t take the discount?!") } ``` And that’s it! For more info see [the full source][burgersource] for this example as well as [the docs][docs]. If you need more dynamic forms that change based on input from previous fields, check out the [dynamic forms](#dynamic-forms) example. [burgersource]: ./examples/burger/main.go [docs]: https://pkg.go.dev/charm.land/huh/v2?tab=doc ## Field Reference - [`Input`](#input): single line text input - [`Text`](#text): multi-line text input - [`Select`](#select): select an option from a list - [`MultiSelect`](#multiple-select): select multiple options from a list - [`Confirm`](#confirm): confirm an action (yes or no) > [!TIP] > Just want to prompt the user with a single field? Each field has a `Run` > method that can be used as a shorthand for gathering quick and easy input. ```go var name string huh.NewInput(). Title("What’s your name?"). Value(&name). Run() // this is blocking... fmt.Printf("Hey, %s!\n", name) ``` ### Input Prompt the user for a single line of text. ```go huh.NewInput(). Title("What’s for lunch?"). Prompt("?"). Validate(isFood). Value(&lunch) ``` ### Text Prompt the user for multiple lines of text. ```go huh.NewText(). Title("Tell me a story."). Validate(checkForPlagiarism). Value(&story) ``` ### Select Prompt the user to select a single option from a list. ```go huh.NewSelect[string](). Title("Pick a country."). Options( huh.NewOption("United States", "US"), huh.NewOption("Germany", "DE"), huh.NewOption("Brazil", "BR"), huh.NewOption("Canada", "CA"), ). Value(&country) ``` ### Multiple Select Prompt the user to select multiple (zero or more) options from a list. ```go huh.NewMultiSelect[string](). Options( huh.NewOption("Lettuce", "Lettuce").Selected(true), huh.NewOption("Tomatoes", "Tomatoes").Selected(true), huh.NewOption("Charm Sauce", "Charm Sauce"), huh.NewOption("Jalapeños", "Jalapeños"), huh.NewOption("Cheese", "Cheese"), huh.NewOption("Vegan Cheese", "Vegan Cheese"), huh.NewOption("Nutella", "Nutella"), ). Title("Toppings"). Limit(4). Value(&toppings) ``` ### Confirm Prompt the user to confirm (Yes or No). ```go huh.NewConfirm(). Title("Are you sure?"). Affirmative("Yes!"). Negative("No."). Value(&confirm) ``` ## Accessibility `huh?` has a special rendering option designed specifically for screen readers. You can enable it with `form.WithAccessible(true)`. > [!TIP] > We recommend setting this through an environment variable or configuration > option to allow the user to control accessibility. ```go accessibleMode := os.Getenv("ACCESSIBLE") != "" form.WithAccessible(accessibleMode) ``` Accessible forms will drop TUIs in favor of standard prompts, providing better dictation and feedback of the information on screen for the visually impaired. ## Themes `huh?` contains a powerful theme abstraction. Supply your own custom theme or choose from one of the five predefined themes: - `Charm` - `Dracula` - `Catppuccin` - `Base 16` - `Default`

Themes can take advantage of the full range of [Lip Gloss][lipgloss] style options. For a high level theme reference see [the docs](https://pkg.go.dev/charm.land/huh/v2#Theme). [lipgloss]: https://github.com/charmbracelet/lipgloss ## Dynamic Forms `huh?` forms can be as dynamic as your heart desires. Simply replace properties with their equivalent `Func` to recompute the properties value every time a different part of your form changes. Here’s how you would build a simple country + state / province picker. First, define some variables that we’ll use to store the user selection. ```go var country string var state string ``` Define your country select as you normally would: ```go huh.NewSelect[string](). Options(huh.NewOptions("United States", "Canada", "Mexico")...). Value(&country). Title("Country"). ``` Define your state select with `TitleFunc` and `OptionsFunc` instead of `Title` and `Options`. This will allow you to change the title and options based on the selection of the previous field, i.e. `country`. To do this, we provide a `func() string` and a `binding any` to `TitleFunc`. The function defines what to show for the title and the binding specifies what value needs to change for the function to recompute. So if `country` changes (e.g. the user changes the selection) we will recompute the function. For `OptionsFunc`, we provide a `func() []Option[string]` and a `binding any`. We’ll fetch the country’s states, provinces, or territories from an API. `huh` will automatically handle caching for you. > [!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. ```go 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. ```go err := form.Run() if err != nil { log.Fatal(err) } ``` ## Bonus: Spinner `huh?` ships with a standalone spinner package. It’s useful for indicating background activity after a form is submitted. Create a new spinner, set a title, set the action (or provide a `Context`), and run the spinner: Action Style Context Style ```go err := spinner.New(). Title("Making your burger..."). Action(makeBurger). Run() fmt.Println("Order up!") ``` ```go go makeBurger() err := spinner.New(). Type(spinner.Line). Title("Making your burger..."). Context(ctx). Run() fmt.Println("Order up!") ``` For more on Spinners see the [spinner examples](./examples/spinner) and [the spinner docs](https://pkg.go.dev/charm.land/huh/v2/spinner). ## What about Bubble Tea? Huh is built on [Bubble Tea][tea] and, in addition to its standalone mode, `huh?` has first-class support and can be easily integrated into Bubble Tea applications. It’s very useful in portions of your Bubble Tea application that need form-like input, and for times when you need more flexibility than `huh?` alone can offer. A `huh.Form` is just a `tea.Model`, so you can use it just as you would any other [Bubble](https://github.com/charmbracelet/bubbles). ``` … ``` For more info in using `huh?` in Bubble Tea applications see [the full Bubble Tea example][example]. [tea]: https://github.com/charmbracelet/bubbletea [bubbles]: https://github.com/charmbracelet/bubbles [example]: https://github.com/charmbracelet/huh/blob/main/examples/bubbletea/main.go ## `Huh?` in the Wild For some `Huh?` programs in production, see: * [glyphs](https://github.com/maaslalani/glyphs): a unicode symbol picker * [meteor](https://github.com/stefanlogue/meteor): a highly customisable conventional commit message tool * [freeze](https://github.com/charmbracelet/freeze): a tool for generating images of code and terminal output * [savvy](https://github.com/getsavvyinc/savvy-cli): the easiest way to create, share, and run runbooks in the terminal ## Contributing See [contributing][contribute]. [contribute]: https://github.com/charmbracelet/huh/contribute ## Feedback We’d love to hear your thoughts on this project. Feel free to drop us a note! - [Twitter](https://twitter.com/charmcli) - [The Fediverse](https://mastodon.social/@charmcli) - [Discord](https://charm.sh/chat) ## Acknowledgments `huh?` is inspired by the wonderful [Survey][survey] library by Alec Aivazis. [survey]: https://github.com/AlecAivazis/survey ## License [MIT](https://github.com/charmbracelet/bubbletea/raw/master/LICENSE) --- Part of [Charm](https://charm.sh). Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة

GitHub Issues· 0 open

View all on GitHub

No open issues yet, or sync has not completed.

Highlights

  • •Input: single line text input
  • •Text: multi-line text input
  • •Select: select an option from a list
  • •MultiSelect: select multiple options from a list
  • •Confirm: confirm an action (yes or no)
  • •Catppuccin
  • •glyphs: a unicode symbol picker
  • •meteor: a highly customisable conventional commit message tool
  • •freeze: a tool for generating images of code and terminal output
  • •savvy: the easiest way to create, share, and run runbooks in the terminal

> Tags

Go

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言