Skip to main content

How to Implement Arrow Key Navigation in a Menu in Batch Script

Standard Batch menus rely on the user typing a key and pressing Enter, or using the choice command for single-key input. However, the most intuitive way to navigate a list is using the Arrow Keys and the Enter key. While the native Batch environment doesn't directly return "Arrow Key" codes, we can use a small PowerShell helper inside our script to capture these inputs and create a modern, high-end menu experience.

In this guide, we will demonstrate how to build a menu with a highlighted selection that moves up and down using the arrow keys.

The Strategy: The Hybrid Input Loop

Since Batch cannot easily detect the "Up Arrow" key, our script will:

  1. Use a variable (!selected!) to track the current index.
  2. Call a small PowerShell one-liner to wait for a keypress.
  3. PowerShell will return a specific string (like "UpArrow", "DownArrow", or "Enter") back to our Batch script.
  4. Batch will update the index and redraw the screen.

Implementation Script

This script creates a 3-item menu that you can navigate with Up/Down and select with Enter.

@echo off
setlocal enabledelayedexpansion

:: 1. Setup ANSI Colors
for /F %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"

if not defined ESC (
echo [ERROR] Could not generate ANSI escape character.
pause
exit /b 1
)

set "HL=!ESC![7m"
set "RS=!ESC![0m"

:: Define menu items using indexed variables
set "item1=Start Application"
set "item2=Settings"
set "item3=Exit"
set "itemCount=3"

set "selection=1"

:menu_loop
cls
echo ==========================================
echo ARROW KEY NAVIGATION MENU
echo ==========================================
echo Use Up/Down arrows and press ENTER
echo ------------------------------------------

:: 2. Display items with highlight on the selected item
for /L %%i in (1, 1, !itemCount!) do (
if !selection!==%%i (
echo !HL! [%%i] !item%%i! !RS!
) else (
echo [%%i] !item%%i!
)
)

echo ------------------------------------------

:: 3. Capture Arrow Keys via PowerShell
for /F "delims=" %%K in ('powershell -nologo -noprofile -command "[Console]::ReadKey($true).Key"') do set "key=%%K"

:: 4. Process the key
if "!key!"=="UpArrow" (
set /a "selection-=1"
if !selection! lss 1 set "selection=!itemCount!"
)
if "!key!"=="DownArrow" (
set /a "selection+=1"
if !selection! gtr !itemCount! set "selection=1"
)
if "!key!"=="Enter" goto :action

goto menu_loop

:action
:: Handle the Exit option
if !selection!==!itemCount! (
echo.
echo Exiting...
endlocal
exit /b
)

echo.
echo You selected: !item%selection%!
pause
goto menu_loop

How the PowerShell Integration Works

The magic happens in this line:

powershell -nologo -noprofile -command "[Console]::ReadKey($true).Key"
  • [Console]::ReadKey($true): Tells the console to stop and wait for a single keypress. The $true parameter suppresses the character from being echoed to the screen.
  • .Key: Returns the name of the key pressed (e.g., UpArrow, DownArrow, Enter, A, Escape).
  • for /F "delims=" %%K in (...): Batch captures the output of this PowerShell command and stores it in the key variable.
  • -nologo -noprofile: These flags suppress the PowerShell startup banner and skip loading the user's profile, reducing the delay between keypresses.
info

Performance note: Each keypress spawns a new PowerShell process, which introduces a small delay (typically 200–500ms). This is noticeable as a brief pause after each key press before the menu redraws. For most interactive menus this is acceptable, but it means the menu will not feel as instantaneous as a native GUI application.

Dynamic Item Count

The implementation above uses indexed variables (item1, item2, item3) and a for /L loop, making it easy to add or remove items without modifying the display logic. To add a new option, simply define a new itemN variable and increment itemCount:

set "item4=View Logs"
set "item5=Restart Service"
set "itemCount=5"

The display loop, boundary wrapping, and highlight logic all adapt automatically.

Improving Appearance with ANSI

We use ESC[7m which is the Reverse Video (Invert Colors) ANSI code. This swaps the foreground and background colors of the selected line, creating a "highlight bar" that moves as the user presses the arrow keys. The ESC[0m reset code returns subsequent text to normal formatting.

You can also combine the highlight with color codes for a more distinctive selection:

:: Green highlighted selection bar
set "HL=!ESC![7;92m"
set "RS=!ESC![0m"

Summary Checklist

  1. Selection Tracking: Use a variable to store which line is active.
  2. Boundary Logic: Ensure the selection wraps around (e.g., pressing Up at Item 1 jumps to the last item, pressing Down at the last item jumps to Item 1).
  3. PowerShell Listener: Use the [Console]::ReadKey($true).Key method for reliable arrow key detection.
  4. Dynamic Rendering: Use a for /L loop with indexed variables so adding menu items requires no changes to the display logic.
  5. Action Handling: Use goto or call to trigger logic based on the final selection once Enter is pressed.

Conclusion

By bridging Batch with a tiny bit of PowerShell, you can unlock rich user interaction patterns that were previously impossible. Arrow key navigation turns a simple menu into a fully interactive terminal application, providing your users with a familiar, intuitive, and modern way to control your automation tools.