How to Remap Modifier Keys and Hardware Keyboards Using hidutil in macOS

Switching between Windows, Linux, and macOS keyboards frequently causes muscle memory friction, particularly with the placement of the Command, Control, Option, and Caps Lock keys. While macOS provides basic modifier key remapping inside System Settings under Keyboard > Keyboard Shortcuts > Modifier Keys, this graphical interface only supports a handful of pre-selected modifier actions. For power users, software developers, and mechanical keyboard enthusiasts who require custom key swapping—such as converting Caps Lock into a dedicated Escape or Hyper key, or remapping non-standard external mechanical keyboards—the native macOS command-line utility hidutil provides kernel-level remapping without installing heavy third-party background software like Karabiner-Elements.

Understanding the HID Event Architecture and Keycode Offsets

The hidutil (Human Interface Device Utility) tool operates directly within macOS’s IOKit framework. When a key is pressed, the hardware controller sends an 8-bit USB HID Usage ID. hidutil intercepts this event and translates it using a 64-bit mask structure:

  • The high 32 bits represent the USB HID Usage Page: 0x700000000 corresponds to the standard Keyboard Usage Page.
  • The low 32 bits represent the specific key Usage ID.

Common HID Usage IDs include:

  • Caps Lock: 0x39 (Full 64-bit ID: 0x700000039)
  • Escape: 0x29 (Full 64-bit ID: 0x700000029)
  • Left Control: 0xE0 (Full 64-bit ID: 0x7000000E0)
  • Left Command: 0xE3 (Full 64-bit ID: 0x7000000E3)
  • Left Option / Alt: 0xE2 (Full 64-bit ID: 0x7000000E2)
  • Right Command: 0xE7 (Full 64-bit ID: 0x7000000E7)

Remapping Caps Lock to Escape via the Terminal

A classic configuration for programmers and Vim users is transforming the underutilised Caps Lock key into a convenient Escape key:

  1. Open Terminal via Spotlight or Applications > Utilities.
  2. Execute the following hidutil property --set command:
hidutil property --set '{"UserKeyMapping":[
  {"HIDKeyboardModifierMappingSrc":0x700000039,"HIDKeyboardModifierMappingDst":0x700000029}
]}'

The modification takes effect instantly across all connected keyboards. Pressing the physical Caps Lock key immediately emits a hardware Escape signal. The change requires no root permissions, does not consume background battery, and introduces zero input latency.

Swapping Command and Control for Windows Keyboards

If you connect a standard PC keyboard where the physical Alt and Windows keys conflict with Mac conventions, swap Left Control and Left Command:

hidutil property --set '{"UserKeyMapping":[
  {"HIDKeyboardModifierMappingSrc":0x7000000E0,"HIDKeyboardModifierMappingDst":0x7000000E3},
  {"HIDKeyboardModifierMappingSrc":0x7000000E3,"HIDKeyboardModifierMappingDst":0x7000000E0}
]}'

This bidirectional mapping binds Left Control to emit Left Command, and Left Command to emit Left Control.

Checking Active Mappings and Resetting Key Defaults

To inspect your current active mappings:

hidutil property --get "UserKeyMapping"

To completely clear all active remappings and restore hardware defaults:

hidutil property --set '{"UserKeyMapping":[]}'

Persisting Key Remappings Across Reboots with a LaunchAgent

Commands executed via hidutil property --set apply only to the active login session and reset when macOS restarts. To make your key remapping permanent across reboots, create a user LaunchAgent:

  1. Create a LaunchAgent plist file in your user library:
mkdir -p ~/Library/LaunchAgents
nano ~/Library/LaunchAgents/com.user.hidutil.plist
  1. Paste the following XML configuration (example configuring Caps Lock to Escape):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.user.hidutil</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/bin/hidutil</string>
    <string>property</string>
    <string>--set</string>
    <string>{"UserKeyMapping":[{"HIDKeyboardModifierMappingSrc":0x700000039,"HIDKeyboardModifierMappingDst":0x700000029}]}</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
</dict>
</plist>
  1. Save the file by pressing Ctrl + O and Enter, then exit with Ctrl + X.
  2. Load the LaunchAgent to activate it immediately and persist it through future restarts:
launchctl load ~/Library/LaunchAgents/com.user.hidutil.plist

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.