How to Use Gum: The Beginner’s Guide to Glamorous Shell Scripts
CodexFuse Team · 2026-11-02T02:36:04.902Z · 14 min read min read
Gum is Charm’s CLI toolkit that makes shell scripts glamorous: this beginner’s guide covers installing it, choose, input, confirm, spin, style, and the stderr lesson, then builds a conventional-commit helper script.
How to Use Gum: The Beginner's Guide to Glamorous Shell Scripts
When you search for how to use gum, the first thing to know is that this guide is not about chewing gum — it is about Gum, the command-line toolkit from Charm that makes your shell scripts glamorous. This gum tutorial walks you through installing it, learning each command family, and building your first script. If you write bash scripts that lean on plain read prompts and hope the user types the right thing, Gum gives you pickers, confirm dialogs, spinners, and styled output with a single command each.
What Is Gum, Exactly?
Gum is a CLI toolkit from Charm that adds 13 small interactive commands to your shell scripts — so you can build pickers, inputs, confirmations, spinners, tables, and styled output without writing any Go code. It is the fastest way to turn plain automation into interactive shell scripts. Each command does one thing: gum choose shows a list, gum input reads a line of text, gum confirm asks yes or no, gum spin shows a spinner while another command runs, and so on. The full set is choose, confirm, file, filter, format, input, join, pager, spin, style, table, write, and log. It is the tool you reach for when a script needs a human decision but you do not want to build a full terminal UI. Developers who already live in the terminal — the kind who keep a persistent session open with our tmux beginner guide — find Gum a natural next step.
Installing Gum on Your System
Install Gum once, using your platform's normal package flow. The commands below come from the project's README, and the current release is v2.0.2 (the v2 line upgraded the internals to Bubble Tea and Lip Gloss v2 with no user-facing changes, so everything you read here works the same).
| Platform | Install command |
|---|---|
| macOS or Linux (Homebrew) | brew install gum |
| Debian / Ubuntu | Charm apt repo: curl -fsSL https://repo.charm.sh/apt/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpg, then add deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * * to /etc/apt/sources.list.d/charm.list, then sudo apt update && sudo apt install gum |
| Fedora / EPEL 10 | dnf install gum |
| Fedora / RHEL / openSUSE (Charm repo) | sudo rpm --import https://repo.charm.sh/yum/gpg.key, add the charm.repo file from https://repo.charm.sh/yum/, then install with yum or zypper |
| Arch Linux | pacman -S gum |
| Nix | nix-env -iA nixpkgs.gum |
| Flox | flox install gum |
| Windows | winget install charmbracelet.gum or scoop install charm-gum |
| FreeBSD | sudo pkg install gum (or via ports) |
| Go toolchain (any platform) | go install charm.land/gum/v2@latest |
Verify the install with gum -v or gum --version. If you use Homebrew for other command-line tools, our Homebrew beginner guide covers the same install pattern.
Your First Gum Command: gum choose
gum choose is Gum's list picker: it prints a list of options as an interactive menu, and the user's selection comes back as text on stdout. Run this to feel it:
printf '%s' "Pizza
Sushi
Tacos" | gum choose --header "Pick dinner"
You get a navigable list — arrow keys to move, Enter to pick — and the chosen word prints to stdout. That pattern, choice from the menu becomes the value in your script, is the whole idea behind Gum's pickers. Two flags matter immediately: --limit 5 lets the user pick up to five items, and --no-limit lets them pick as many as they like. Also useful: --header for a prompt line above the list, --select-if-one to skip the menu when only one item exists, and --cursor to change the cursor character.
Why NAME=$(gum input) Works: The Stderr Lesson
Gum renders its interactive UI to stderr and prints only the final answer to stdout — which is exactly why NAME=$(gum input) captures the typed value and nothing else. This split is the single most important thing to understand about Gum. The menu, the prompt, the spinner animation: all of it goes to stderr, where your terminal shows it but your pipes never see it. The data — the selected choice, the typed line, the confirmed answer — goes to stdout, so it flows into variables and pipes cleanly. Two exit-code facts round out the picture: pressing Ctrl+C during a Gum prompt exits with code 130, and a prompt that hits its --timeout writes "timed out" to stderr and exits 124.
Asking for Text: gum input and gum write
gum input reads one line of text with a prompt, placeholder, and optional default value; gum write opens a small multi-line editor for longer answers. A polished prompt looks like this:
NAME=$(gum input --prompt "* " --placeholder "Your name" --value "Ada")
The --placeholder shows ghost text before the user types, --value pre-fills an answer they can edit, and --prompt changes the leading character. For sensitive input, gum input --password masks the typing. Styling comes along for free: flags like --cursor.foreground "#FF0" and --width 80 tune the look. When one line is not enough — a commit message body, a note, a description — use gum write, which opens a multi-line text area with line numbers; the finished text prints to stdout when the user confirms.
Yes or No: gum confirm and the Exit-Code Lesson
gum confirm does not print "yes" or "no" — its answer is the exit code: 0 for affirmative, 1 for negative. So you never capture its output; you branch on it:
gum confirm "Delete the old backups?" && rm -rf backups/old
If the user picks yes, the command after && runs; if no, the exit code 1 stops it. Use $? if you need the value explicitly. The flags tune the dialog: --affirmative and --negative rename the two buttons ("Ship it" / "Hold"), --default pre-selects an option, and --timeout auto-answers after N seconds.
Long-Running Commands: gum spin
gum spin wraps any command with an animated spinner, keeping the screen alive while slow work happens. The basic shape is:
gum spin --spinner dot --title "Deploying..." -- ./deploy.sh
The title explains what is happening; the spinner animation shows progress is being made. Choose a spinner style with --spinner — options include line, dot, minidot, jump, pulse, points, globe, moon, monkey, meter, and hamburger. Add --show-output when you want the wrapped command's output visible or piped while it runs. A spinner plus a clear title is the difference between a script that feels frozen and one that feels alive.
Picking Files: gum file and gum filter
gum file is an interactive file picker that walks a directory tree; gum filter is a fuzzy finder over whatever you pipe in. Open an editor on a chosen file with one line:
$EDITOR $(gum file $HOME)
By default Gum picks files; pass --directory to pick directories instead. Useful flags: -a/--all shows hidden dotfiles, --height controls the menu size, and --timeout aborts with no selection after N seconds. For anything that is not the filesystem, pipe text into gum filter and get a searchable list — git branch | gum filter to pick a branch, for example.
Turning Data into Choices: gum table and gum log
gum table turns CSV-style data into a selectable table; gum log prints structured, leveled log lines from your scripts. Feed a CSV through gum table and pick a row:
gum table < flavors.csv
Rows split on the --separator flag (comma by default); --columns lets you name and constrain columns. For script output, gum log gives you levels and structure:
gum log --structured --level debug "Creating file..." name file.txt
This prints a debug-level line with a name=file.txt field. Flags include --level (none, debug, info, warn, error, fatal), --formatter (json, logfmt, or text), --time for timestamp formats, and --file to log to a file instead of the terminal. A script that logs with levels is dramatically easier to debug than one that echoes bare text.
Laying Out Output: gum join, gum style, and gum format
Gum's layout commands — join, style, and format — compose and beautify your script's output without printf gymnastics. gum join places text blocks side by side (horizontally by default; --vertical stacks them, --align centers or edges them). gum style adds borders and color to a single block:
gum style --foreground 212 --border-foreground 212 --border double --align center --width 50 --margin "1 2" --padding "2 4" 'Deploy finished' 'Everything is green.'
Colors accept ANSI codes, hex values, and names; --border takes styles like double and normal. gum format renders markdown, code, templates, and emoji passed on stdin:
gum format <<'EOF'
# Gum Formats
- Markdown
- Code
- Template
- Emoji
EOF
Use --type (or -t) to pick between markdown (the default), code, template, and emoji. Printing a readable summary at the end of a script is the cheapest way to make it feel professional, and gum format does it in one line.
Your First Script: A Conventional-Commit Helper
The fastest way to learn Gum is to build one script that chains choose, input, write, and confirm into a conventional-commit helper. This five-step arc uses four Gum commands the way real scripts do:
- Choose the commit type. Feed the type list into
gum chooseand capture the result:TYPE=$(gum choose --header "Commit type" <<'EOF' feat fix docs refactor test EOF ) - Input the scope.
SCOPE=$(gum input --placeholder "scope" --prompt "* ")reads one word likeauth. - Write the body.
BODY=$(gum write --placeholder "What changed and why...")collects the longer explanation. - Confirm before committing. Show the assembled message with
gum styleorgum format, then gate the commit:gum confirm "Commit as $TYPE$SCOPE?" || exit - Commit.
git commit -m "$TYPE($SCOPE): $SUMMARY" -m "$BODY"runs only after the user said yes.
Every input is validated by construction — the type can only be one of the list items, the scope and body come from guided prompts, and nothing commits without an explicit yes. If you are new to Git itself, our Git beginner guide covers the commit model this script assumes.
Skimming Long Output: gum pager
gum pager scrolls long text through a built-in viewport with line numbers — it is not a wrapper around less. Run gum pager < README.md and you get a navigable, styled viewer implemented in Bubble Tea, with no external pager dependency to install. Reach for it at the end of a script when the output is longer than a screen: print the summary with gum style, and offer the full log through gum pager.
The 13 Gum Commands at a Glance
Gum ships 13 user commands: choose, confirm, file, filter, format, input, join, pager, spin, style, table, write, and log. This table maps each one to its job and a signature example so you can see the whole toolkit on one page.
| Command | What it does | Signature example |
|---|---|---|
gum choose | Interactive list picker; selection to stdout | gum choose "Pizza" "Sushi" "Tacos" |
gum confirm | Yes/no dialog; answer is the exit code (0/1) | gum confirm "Ship it?" && deploy |
gum file | Interactive file picker walking a directory tree | $EDITOR $(gum file $HOME) |
gum filter | Fuzzy finder over piped input | git branch | gum filter |
gum format | Renders markdown, code, templates, emoji | cat NOTES.md | gum format |
gum input | Single-line text prompt with placeholder and default | NAME=$(gum input --placeholder "name") |
gum join | Joins styled blocks horizontally (or vertically) | gum join --vertical "$A" "$B" |
gum pager | Built-in viewport pager with line numbers, not less | gum pager < long.log |
gum spin | Animated spinner around a long-running command | gum spin --title "Working..." -- make |
gum style | Borders, colors, padding, and margins for text | gum style --border double "Done" |
gum table | Selectable table from CSV-style data | gum table < flavors.csv |
gum write | Multi-line text editor area for longer answers | BODY=$(gum write --placeholder "notes") |
gum log | Leveled, structured log lines for scripts | gum log --level info "saved" |
Gum vs Whiptail and Dialog
If you know whiptail or dialog — the classic terminal dialog-box utilities of the 1990s — Gum is the modern successor: same idea (dialogs from shell scripts), but with per-command binaries, fuzzy filtering, styled output, and a component model built on Charm's Bubble Tea. Whiptail and dialog draw box-drawn widgets and return results through file descriptors and exit codes; Gum does the same job with prettier rendering and a much smaller per-widget API — one command per widget instead of one giant flag soup. Two other Charm tools share the ecosystem but never compete with Gum: glow, Charm's terminal-based markdown reader, and vhs, their CLI home-video recorder that turns terminal sessions into GIFs for docs and demos (the Gum README's own demo GIFs are vhs recordings).
7 Beginner Mistakes to Avoid
| Mistake | What happens | The fix |
|---|---|---|
Capturing gum confirm output | confirm prints nothing — your variable is empty and the real answer (the exit code) is lost | Branch on it: gum confirm "Go?" && go or check $? |
| Forgetting the stderr/stdout split | Piping a script and wondering why the menu text shows in the terminal but never in the pipe | Remember: the UI renders to stderr, the data prints to stdout — pipes only ever see the data |
Using --print on gum table | That flag does not exist on the current gum table command | Use --separator and --columns; check gum table --help for the real flags |
| Teaching Ctrl+C as success | Ctrl+C exits with code 130, not a clean 0 — scripts that ignore it can leave half-finished state | Handle 130 explicitly, or let set -e stop the script |
Picking directories with plain gum file | Files are selected by default; directories are not | Pass --directory when you want folder picking |
| Installing v1 docs commands | Old guides still show charmbracelet/gum Go paths and pre-v2 layouts | Use go install charm.land/gum/v2@latest; the v2 module path is the current one |
| Building every widget as raw ANSI | Hand-rolled escapes are brittle across terminals and break on resize | One gum style or gum format call replaces the escape-code mess |
Sources
- charmbracelet/gum releases page — v2.0.2 current release ("No more raw ANSI sequences"), v2.0.1, v2.0.0 ("no UX changes", Bubble Tea/Lip Gloss v2, module renamed to
charm.land/gum/v2), v0.17.0 ("Pad Everything"--padding) — https://github.com/charmbracelet/gum/releases - gum README, main branch — full 13-command list, verbatim install matrix,
gum chooseexamples (--limit/--no-limit),gum inputflags (--placeholder,--prompt,--value,--password),gum confirmexit-code semantics,gum spin(--show-output, spinner types),gum file,gum table,gum log --structured,gum join,gum style,gum formatstdin example,gum pager— https://raw.githubusercontent.com/charmbracelet/gum/main/README.md - gum.go, main branch — utility commands
completionandman,-v/--versionflag, pager described as a shell interface for the viewport bubble — https://raw.githubusercontent.com/charmbracelet/gum/main/gum.go - DeepWiki charmbracelet/gum "Getting Started" — stderr rendering for interactive commands, stdout for data output — https://deepwiki.com/charmbracelet/gum/2-getting-started
- DeepWiki charmbracelet/gum "Formatting Commands" — Ctrl+C exits 130,
--timeoutwrites "timed out" to stderr and exits 124 — https://deepwiki.com/charmbracelet/gum/4-formatting-commands - pkg.go.dev gum/format package — stdin markdown input,
-t/--typemarkdown/code/template/emoji — https://pkg.go.dev/github.com/charmbracelet/gum/format - pkg.go.dev gum/table package —
-s/--separator(default ","),-c/--columns— https://pkg.go.dev/github.com/charmbracelet/gum/table - pkg.go.dev gum/log package —
-s/--structured,-f/--format,--formatter(json/logfmt/text),-l/--level,-t/--time,-o/--file— https://pkg.go.dev/github.com/charmbracelet/gum/log - pkg.go.dev gum/choose package —
--ordered,--header,--select-if-one— https://pkg.go.dev/github.com/charmbracelet/gum/choose - tech.aufomm.com personal guide (~July 2026) — newest independent usage walkthrough found;
gum confirm --helpflags (--affirmative/--negative/--default/--timeout) — https://tech.aufomm.com/how-do-i-use-charmbracelet-gum-to-improve-my-scripts/ - hexmos.com freedevtools gum tldr — command examples; whiptail/dialog cross-referenced under "See also" — https://hexmos.com/freedevtools/tldr/common/gum/
- x-cmd.com gum install page — product/setup page, install commands plus feature summary — https://www.x-cmd.com/install/gum/
- Maciej Walkowiak, "Beautiful bash scripts with gum" (Nov 2022) — 4-year-old personal guide predating table/log/pager/format — https://maciejwalkowiak.com/blog/beautiful-bash-scripts-with-gum/
- Trendoceans, "Gum tool Linux" (~2022) — 4-year-old personal guide predating table/log/pager/format — https://trendoceans.com/gum-tool-linux/
Frequently Asked Questions
Is this guide about chewing gum?
No. This guide is about Gum, the command-line toolkit from Charm (charmbracelet/gum) that adds interactive pickers, inputs, spinners, and styled output to shell scripts. Search results for "how to use gum" are heavily polluted by chewing-gum and dental content; adding "charm" or "cli" to your query disambiguates.
What is gum charm used for?
Making shell scripts interactive and readable. Instead of bare read prompts, you get a list picker (gum choose), text prompts (gum input, gum write), yes/no dialogs (gum confirm), spinners (gum spin), file pickers (gum file), CSV tables (gum table), styled output (gum style), and structured logs (gum log) — 13 small commands you call from any shell, with no Go code.
How do I install gum on Linux?
On Debian/Ubuntu, add Charm's apt repo (the GPG key and sources.list steps are on the project's README) and run sudo apt install gum. On Fedora/EPEL, dnf install gum; on Arch, pacman -S gum. On any Linux with Homebrew, brew install gum. Go users can run go install charm.land/gum/v2@latest.
Why does gum confirm not print anything?
Because its answer is the exit code, not text: 0 for affirmative, 1 for negative. Branch on it directly with gum confirm "Delete?" && rm file or inspect $?. Capturing its output into a variable is the most common beginner mistake.
How is gum different from whiptail or dialog?
Same job — dialogs from shell scripts — but modern: one command per widget instead of one giant flag soup, fuzzy filtering (gum filter), CSV table picking (gum table), markdown rendering (gum format), and styled output built on Charm's Bubble Tea and Lip Gloss. The classic tools still work; Gum is the prettier, more composable successor.