Gum - Terminal UI Toolkit

Gum provides highly configurable, ready-to-use utilities for building terminal UIs and interactive scripts.

Gum is a tool for building glamorous shell scripts with pre-built interactive components. Created by Charm, it requires no Go knowledge - just shell scripting.

Installation

brew install gum

Other options: pacman -S gum (Arch), winget install charmbracelet.gum (Windows), go install github.com/charmbracelet/gum@latest.

Styled Output

gum style

Apply colors, borders, padding, and alignment to text:

gum style --foreground 212 "Pikachu"
gum style --foreground 212 --bold "LEGENDARY"
gum style --border double --padding "1 2" "Pokemon Info"

Styled Headers

gum style \
  --foreground 212 --border-foreground 212 --border double \
  --align center --width 50 --margin "1 2" --padding "1 2" \
  "Pokedex v1.0"

Color Functions

# Success/Error/Warning messages
gum style --foreground 42 "Caught Pikachu!"      # Green
gum style --foreground 196 "Pokemon fled!" >&2   # Red
gum style --foreground 214 "Low HP warning"      # Yellow
gum style --foreground 99 "Info message"         # Purple

User Input

gum input

Single-line text input:

NAME=$(gum input --placeholder "Pokemon name")
echo "You entered: $NAME"

With validation and defaults:

gum input --placeholder "Enter HP" --value "100"
gum input --password --placeholder "API Key"
gum input --char-limit 20 --width 30

gum write

Multi-line text input (Ctrl+D to finish):

DESCRIPTION=$(gum write --placeholder "Pokemon description...")

Selection

gum choose

Select from a list of options:

TYPE=$(gum choose "Fire" "Water" "Grass" "Electric")
echo "Selected: $TYPE"

Multiple selection:

TYPES=$(gum choose --no-limit "Fire" "Water" "Grass" "Electric" "Flying")

With limit:

gum choose --limit 2 "Bulbasaur" "Charmander" "Squirtle"

From file or pipe:

cat pokemon.txt | gum choose
ls *.yaml | gum choose

gum filter

Fuzzy-searchable selection:

POKEMON=$(cat pokedex.txt | gum filter --placeholder "Search Pokemon...")

With options:

gum filter --limit 5 --height 10 < pokemon-list.txt

Confirmation

gum confirm

Yes/No prompts (exit code 0 = yes, 1 = no):

gum confirm "Delete Pikachu?" && rm pikachu.yaml || echo "Cancelled"

With default:

gum confirm --default=false "Are you sure?"

Practical pattern:

if gum confirm "Evolve Pikachu to Raichu?"; then
  echo "Evolving..."
else
  echo "Cancelled"
fi

Progress & Spinners

gum spin

Show a spinner during long operations:

gum spin --spinner dot --title "Catching Pokemon..." -- sleep 3

With output:

gum spin --show-output --title "Searching..." -- grep -r "Pikachu" .

Available spinners: line, dot, minidot, jump, pulse, points, globe, moon, monkey, meter, hamburger.

Formatting

gum format

Render markdown, code, and emojis:

echo "# Pikachu" | gum format
echo "**Electric** type" | gum format

Code highlighting:

cat script.sh | gum format -t code

Emoji support:

echo ':zap: Pikachu used Thunder! :boom:' | gum format -t emoji

Practical Examples

Interactive Pokemon Selector

#!/bin/bash

POKEMON=$(ls pokemon/*.yaml | xargs -n1 basename -s .yaml | gum filter)
if [ -n "$POKEMON" ]; then
  cat "pokemon/$POKEMON.yaml"
fi

Pokemon Creator

#!/bin/bash

NAME=$(gum input --placeholder "Pokemon name")
TYPE=$(gum choose "Fire" "Water" "Grass" "Electric" "Psychic")
HP=$(gum input --placeholder "HP" --value "100")
LEGENDARY=$(gum confirm "Is it legendary?" && echo "true" || echo "false")

cat > "pokemon/$NAME.yaml" << EOF
name: $NAME
type: $TYPE
stats:
  hp: $HP
legendary: $LEGENDARY
EOF

gum style --foreground 42 "Created $NAME!"

Batch Delete with Confirmation

#!/bin/bash

FILES=$(ls *.yaml | gum choose --no-limit)
if [ -n "$FILES" ]; then
  echo "$FILES" | while read f; do
    gum confirm "Delete $f?" && rm "$f"
  done
fi

Styled Menu

#!/bin/bash

gum style --border double --padding "1 2" "Pokedex Menu"
ACTION=$(gum choose "List Pokemon" "Add Pokemon" "Search" "Exit")

case "$ACTION" in
  "List Pokemon") ls pokemon/ ;;
  "Add Pokemon") ./add-pokemon.sh ;;
  "Search") gum filter < pokemon-names.txt ;;
  "Exit") exit 0 ;;
esac

Handling Piped Input

When stdin is already in use, redirect gum from /dev/tty:

cat pokemon.txt | while read pokemon; do
  gum confirm "Process $pokemon?" </dev/tty && echo "Processing $pokemon"
done

Customization

Set colours via flags or environment variables:

# Via flags
gum input --cursor.foreground "#FF0" --prompt.foreground "#0FF"

# Via environment
export GUM_INPUT_CURSOR_FOREGROUND="#FF0"
gum input

Tips

  1. Use </dev/tty when piping data and need interactive input
  2. Chain commands: TYPE=$(gum choose ...) && gum confirm ...
  3. Use --no-limit for multi-select with gum choose
  4. Style error messages with >&2 to send to stderr
  5. Combine with Yq and Jq for data manipulation

Resources