Survey
A golang library for building interactive and accessible prompts with full support for windows and posix terminals.
Install / Use
/learn @AlecAivazis/SurveyREADME
Survey
A library for building interactive and accessible prompts on terminals supporting ANSI escape sequences.
⚠️ This project is no longer maintained. For an alternative, please check out: https://github.com/charmbracelet/bubbletea ⚠️
Hey everyone! I finally came to terms with the fact that I can no longer dedicate enough time to keep this library alive. This project outgrew my wildest expectations and was such a great experience. If someone else wants to take over maintainence, please reach out
<img width="550" src="https://thumbs.gfycat.com/VillainousGraciousKouprey-size_restricted.gif"/>package main
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
)
// the questions to ask
var qs = []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{Message: "What is your name?"},
Validate: survey.Required,
Transform: survey.Title,
},
{
Name: "color",
Prompt: &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
Default: "red",
},
},
{
Name: "age",
Prompt: &survey.Input{Message: "How old are you?"},
},
}
func main() {
// the answers will be written to this struct
answers := struct {
Name string // survey will match the question and field names
FavoriteColor string `survey:"color"` // or you can tag fields to match a specific name
Age int // if the types don't match, survey will convert it
}{}
// perform the questions
err := survey.Ask(qs, &answers)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("%s chose %s.", answers.Name, answers.FavoriteColor)
}
Examples
Examples can be found in the examples/ directory. Run them
to see basic behavior:
go run examples/simple.go
go run examples/validation.go
Running the Prompts
There are two primary ways to execute prompts and start collecting information from your users: Ask and
AskOne. The primary difference is whether you are interested in collecting a single piece of information
or if you have a list of questions to ask whose answers should be collected in a single struct.
For most basic usecases, Ask should be enough. However, for surveys with complicated branching logic,
we recommend that you break out your questions into multiple calls to both of these functions to fit your needs.
Configuring the Prompts
Most prompts take fine-grained configuration through fields on the structs you instantiate. It is also
possible to change survey's default behaviors by passing AskOpts to either Ask or AskOne. Examples
in this document will do both interchangeably:
prompt := &Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
// can pass a validator directly
Validate: survey.Required,
}
// or define a default for the single call to `AskOne`
// the answer will get written to the color variable
survey.AskOne(prompt, &color, survey.WithValidator(survey.Required))
// or define a default for every entry in a list of questions
// the answer will get copied into the matching field of the struct as shown above
survey.Ask(questions, &answers, survey.WithValidator(survey.Required))
Prompts
Input
<img src="https://thumbs.gfycat.com/LankyBlindAmericanpainthorse-size_restricted.gif" width="400px"/>name := ""
prompt := &survey.Input{
Message: "ping",
}
survey.AskOne(prompt, &name)
Suggestion Options
<img src="https://i.imgur.com/Q7POpA1.gif" width="800px"/>file := ""
prompt := &survey.Input{
Message: "inform a file to save:",
Suggest: func (toComplete string) []string {
files, _ := filepath.Glob(toComplete + "*")
return files
},
}
}
survey.AskOne(prompt, &file)
Multiline
<img src="https://thumbs.gfycat.com/ImperfectShimmeringBeagle-size_restricted.gif" width="400px"/>text := ""
prompt := &survey.Multiline{
Message: "ping",
}
survey.AskOne(prompt, &text)
Password
<img src="https://thumbs.gfycat.com/CompassionateSevereHypacrosaurus-size_restricted.gif" width="400px" />password := ""
prompt := &survey.Password{
Message: "Please type your password",
}
survey.AskOne(prompt, &password)
Confirm
<img src="https://thumbs.gfycat.com/UnkemptCarefulGermanpinscher-size_restricted.gif" width="400px"/>name := false
prompt := &survey.Confirm{
Message: "Do you like pie?",
}
survey.AskOne(prompt, &name)
Select
<img src="https://thumbs.gfycat.com/GrimFilthyAmazonparrot-size_restricted.gif" width="450px"/>color := ""
prompt := &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
}
survey.AskOne(prompt, &color)
Fields and values that come from a Select prompt can be one of two different things. If you pass an int
the field will have the value of the selected index. If you instead pass a string, the string value selected
will be written to the field.
The user can also press esc to toggle the ability cycle through the options with the j and k keys to do down and up respectively.
By default, the select prompt is limited to showing 7 options at a time and will paginate lists of options longer than that. This can be changed a number of ways:
// as a field on a single select
prompt := &survey.MultiSelect{..., PageSize: 10}
// or as an option to Ask or AskOne
survey.AskOne(prompt, &days, survey.WithPageSize(10))
Select options description
The optional description text can be used to add extra information to each option listed in the select prompt:
color := ""
prompt := &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
Description: func(value string, index int) string {
if value == "red" {
return "My favorite color"
}
return ""
},
}
survey.AskOne(prompt, &color)
// Assuming that the user chose "red - My favorite color":
fmt.Println(color) //=> "red"
MultiSelect

days := []string{}
prompt := &survey.MultiSelect{
Message: "What days do you prefer:",
Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
}
survey.AskOne(prompt, &days)
Fields and values that come from a MultiSelect prompt can be one of two different things. If you pass an int
the field will have a slice of the selected indices. If you instead pass a string, a slice of the string values
selected will be written to the field.
The user can also press esc to toggle the ability cycle through the options with the j and k keys to do down and up respectively.
By default, the MultiSelect prompt is limited to showing 7 options at a time and will paginate lists of options longer than that. This can be changed a number of ways:
// as a field on a single select
prompt := &survey.MultiSelect{..., PageSize: 10}
// or as an option to Ask or AskOne
survey.AskOne(prompt, &days, survey.WithPageSize(10))
Editor
Launches the user's preferred editor (defined by the $VISUAL or $EDITOR environment variables) on a temporary file. Once the user exits their editor, the contents of the temporary file are read in as the result. If neither of those are present, notepad (on Windows) or vim (Linux or Mac) is used.
You can also specify a pattern for the name of the temporary file. This can be useful for ensuring syntax highlighting matches your usecase.
prompt := &survey.Editor{
Message: "Shell code snippet",
FileName: "*.sh",
}
survey.AskOne(prompt, &content)
Filtering Options
By default, the user can filter for options in Select and MultiSelects by typing while the prompt is active. This will filter out all options that don't contain the typed string anywhere in their name, ignoring case.
A custom filter function can also be provided to change this behavior:
func myFilter(filterValue string, optValue string, optIndex int) bool {
// only include the option if it includes the filter and has length greater than 5
return strings.Contains(optValue, filterValue) && len(optValue) >= 5
}
// configure it for a specific prompt
&Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
Filter: myFilter,
}
// or define a default for all of the questions
survey.AskOne(prompt, &color, survey.WithFilter(myFilter))
Keeping the filter active
By default the filter will disappear if the user selects one of the filtered elements. Once the user selects one element the filter setting is gone.
However the user can prevent this from happening and keep the filter active for multiple selections in a e.g. MultiSelect:
// configure it for a specific prompt
&Select{
Message: "Choose a color:",
Options: []string{"light-green", "green", "dark-green", "red"},
KeepFilter: true,
}
// or define a default for all of the questions
survey.AskOne(prompt, &color, survey.WithKeepFilter(true))
Validation
Validating individual responses for a particular question can be done by defining a
Validate field on the survey.Question to be validated. This function takes an
interface{} type and returns an error to show to the user, prompting them for another
response. Like usual, validators can be provided directly to the prompt or with survey.WithValidator:
q := &survey.Question{
Prompt: &survey.Input{Message: "Hello world validation"},
Validate: func (val interface{}) error {
// s
