Skip to main content
Custom widgets allow you to extend Dracula tmux with your own functionality by writing bash scripts.

How custom widgets work

Custom widgets are bash scripts placed in the Dracula tmux scripts directory. They’re executed periodically to gather and display information in the status bar.

Creating a custom widget

Script naming pattern

To use a custom widget, reference it in your configuration with the custom: prefix:
set -g @dracula-plugins "custom:my-script.sh"
This tells Dracula to look for my-script.sh in the scripts directory.

Script location

Place your custom script in the Dracula tmux scripts directory:
~/.tmux/plugins/tmux/scripts/my-script.sh
Or if using a custom plugin path:
/path/to/dracula/tmux/scripts/my-script.sh
Make sure your script has execute permissions: chmod +x my-script.sh

Basic script template

Here’s a minimal template for a custom widget:
#!/usr/bin/env bash
# setting the locale, some users have issues with different locales
export LC_ALL=en_US.UTF-8

# Source utilities
current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $current_dir/utils.sh

main()
{
  # Your logic here
  echo "Your output"
}

# Run main function
main

Accessing tmux options

Use the get_tmux_option utility function to read configuration values:
main()
{
  # Get custom option with default value
  label=$(get_tmux_option "@dracula-custom-label" "Default")
  
  # Get refresh rate
  RATE=$(get_tmux_option "@dracula-refresh-rate" 5)
  
  echo "$label Your data here"
  sleep $RATE
}
Then in your .tmux.conf:
set -g @dracula-custom-label "My Widget:"
set -g @dracula-refresh-rate 5

Example custom widgets

Example 1: Display current directory

#!/usr/bin/env bash
export LC_ALL=en_US.UTF-8

current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $current_dir/utils.sh

get_current_dir()
{
  # Get the current pane's directory
  for i in $(tmux list-panes -F "#{pane_active} #{pane_current_path}");
  do
    if [ "$i" == "1" ]; then
      nextone="true"
    elif [ "$nextone" == "true" ]; then
      basename "$i"
      return
    fi
  done
}

main()
{
  label=$(get_tmux_option "@dracula-cwd-label" "📁")
  dir=$(get_current_dir)
  echo "$label $dir"
}

main
Configuration:
set -g @dracula-plugins "custom:current-dir.sh"
set -g @dracula-cwd-label "📁"

Example 2: Display disk usage

#!/usr/bin/env bash
export LC_ALL=en_US.UTF-8

current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $current_dir/utils.sh

get_disk_usage()
{
  case $(uname -s) in
    Linux)
      df -h / | awk 'NR==2 {print $5}'
      ;;
    Darwin)
      df -h / | awk 'NR==2 {print $5}'
      ;;
    *)
      echo "N/A"
      ;;
  esac
}

main()
{
  RATE=$(get_tmux_option "@dracula-refresh-rate" 5)
  label=$(get_tmux_option "@dracula-disk-label" "💾")
  usage=$(get_disk_usage)
  
  echo "$label $usage"
  sleep $RATE
}

main
Configuration:
set -g @dracula-plugins "custom:disk-usage.sh"
set -g @dracula-disk-label "💾"
set -g @dracula-refresh-rate 10

Example 3: Display Docker status

#!/usr/bin/env bash
export LC_ALL=en_US.UTF-8

current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $current_dir/utils.sh

get_docker_status()
{
  if ! command -v docker &> /dev/null; then
    echo "N/A"
    return
  fi
  
  if ! docker info &> /dev/null; then
    echo "OFF"
    return
  fi
  
  running=$(docker ps -q | wc -l | tr -d ' ')
  echo "${running} running"
}

main()
{
  RATE=$(get_tmux_option "@dracula-refresh-rate" 5)
  label=$(get_tmux_option "@dracula-docker-label" "🐳")
  status=$(get_docker_status)
  
  echo "$label $status"
  sleep $RATE
}

main
Configuration:
set -g @dracula-plugins "custom:docker-status.sh"
set -g @dracula-docker-label "🐳"

Example 4: Display todo count

#!/usr/bin/env bash
export LC_ALL=en_US.UTF-8

current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $current_dir/utils.sh

get_todo_count()
{
  todo_file=$(get_tmux_option "@dracula-todo-file" "$HOME/todo.txt")
  
  if [ ! -f "$todo_file" ]; then
    echo "0"
    return
  fi
  
  # Count non-empty lines
  grep -c '.' "$todo_file" 2>/dev/null || echo "0"
}

main()
{
  label=$(get_tmux_option "@dracula-todo-label" "✓")
  count=$(get_todo_count)
  
  if [ "$count" -eq 0 ]; then
    echo ""
  else
    echo "$label $count"
  fi
}

main
Configuration:
set -g @dracula-plugins "custom:todo-count.sh"
set -g @dracula-todo-file "$HOME/Documents/todo.txt"
set -g @dracula-todo-label "✓"

Best practices

Performance considerations

Avoid expensive operations in custom widgets. They run every few seconds according to the refresh rate. Use caching for slow operations.
# Example: Cache slow operations
get_cached_data()
{
  cache_file="/tmp/my-widget-cache"
  cache_duration=60  # seconds
  
  if [ -f "$cache_file" ]; then
    cache_age=$(($(date +%s) - $(stat -f %m "$cache_file" 2>/dev/null || stat -c %Y "$cache_file")))
    
    if [ $cache_age -lt $cache_duration ]; then
      cat "$cache_file"
      return
    fi
  fi
  
  # Expensive operation
  result=$(expensive_command)
  echo "$result" > "$cache_file"
  echo "$result"
}

Error handling

Handle errors gracefully to prevent widget failures:
main()
{
  # Check if required command exists
  if ! command -v some_command &> /dev/null; then
    echo "N/A"
    return
  fi
  
  # Handle command failures
  result=$(some_command 2>/dev/null) || {
    echo "ERROR"
    return
  }
  
  echo "$result"
}

Cross-platform compatibility

Support multiple operating systems:
get_data()
{
  case $(uname -s) in
    Linux)
      # Linux-specific command
      ;;
    Darwin)
      # macOS-specific command
      ;;
    FreeBSD|OpenBSD)
      # BSD-specific command
      ;;
    *)
      echo "Unsupported"
      ;;
  esac
}

Using utilities

The utils.sh file provides helpful functions:
# Get tmux option with default
get_tmux_option "@option-name" "default-value"

# Normalize percentage length
normalize_percent_len "75%"

Testing custom widgets

Test your script directly before adding it to tmux:
# Make script executable
chmod +x ~/.tmux/plugins/tmux/scripts/my-script.sh

# Run script manually
~/.tmux/plugins/tmux/scripts/my-script.sh

# Test with different options
TMUX= ~/.tmux/plugins/tmux/scripts/my-script.sh

Debugging

Add debug output to a log file:
main()
{
  # Debug logging
  echo "[$(date)] Starting widget" >> /tmp/my-widget-debug.log
  
  result=$(get_data)
  echo "[$(date)] Result: $result" >> /tmp/my-widget-debug.log
  
  echo "$result"
}
Then tail the log:
tail -f /tmp/my-widget-debug.log

Complete example

Here’s a complete custom widget with all best practices:
#!/usr/bin/env bash
# Custom widget: Display system load with color coding
export LC_ALL=en_US.UTF-8

current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $current_dir/utils.sh

get_load_average()
{
  case $(uname -s) in
    Linux|Darwin)
      uptime | awk -F'load averages?: ' '{print $2}' | awk '{print $1}' | sed 's/,//'
      ;;
    *)
      echo "N/A"
      ;;
  esac
}

main()
{
  # Get configuration options
  RATE=$(get_tmux_option "@dracula-refresh-rate" 5)
  label=$(get_tmux_option "@dracula-load-label" "LOAD")
  threshold=$(get_tmux_option "@dracula-load-threshold" "2.0")
  
  # Get load average
  load=$(get_load_average)
  
  # Output
  if [ "$load" != "N/A" ]; then
    echo "$label $load"
  else
    echo ""
  fi
  
  sleep $RATE
}

# Run main function
main
Configuration:
set -g @dracula-plugins "custom:load-average.sh"
set -g @dracula-load-label "⚡"
set -g @dracula-load-threshold "3.0"
set -g @dracula-load-average-colors "yellow dark_gray"

Build docs developers (and LLMs) love