i3-style window management on macOS with AeroSpace

AeroSpace brings i3-like tiling, keyboard navigation, and numbered workspaces to macOS. Workspaces can move freely between two displays.

1. Install the apps

Install Homebrew first. AeroSpace is required:

brew install --cask nikitabobko/tap/aerospace

Ghostty and Swift Quit are optional:

brew install --cask swift-quit
brew install --cask ghostty
  • AeroSpace manages windows and workspaces.
  • Swift Quit optionally quits an application after its last window closes.
  • Ghostty is an optional replacement for built-in Terminal.

Open every app once after installation.

2. Allow Accessibility access

Open:

System Settings → Privacy & Security → Accessibility

Enable:

  • AeroSpace
  • Swift Quit, if installed

Restart each app after granting access.

3. Disable conflicting macOS features

Open System Settings → Desktop & Dock:

  1. Turn off Stage Manager.
  2. Turn off window tiling by dragging windows to screen edges.
  3. Turn off the Option-key window tiling shortcut.
  4. Turn off automatic Space rearrangement.
  5. Turn off Displays have separate Spaces for better AeroSpace stability. Log out and back in after changing it.

Open System Settings → Trackpad → More Gestures and disable macOS horizontal Space switching plus the three-finger upward Mission Control gesture. Use AeroSpace keyboard shortcuts instead.

4. Create the AeroSpace config

Create ~/.aerospace.toml:

config-version = 2
start-at-login = true
auto-reload-config = true

after-startup-command = [
    # Optional: remove this line if Swift Quit is not installed.
    'exec-and-forget open -gj -b onebadidea.Swift-Quit',
]

default-root-container-layout = 'tiles'
default-root-container-orientation = 'auto'
accordion-padding = 24
persistent-workspaces = ['1', '2', '3', '4', '5', '6', '7', '8', '9']

on-focused-monitor-changed = ['move-mouse monitor-lazy-center']
focus-follows-mouse.enabled = false
automatically-unhide-macos-hidden-apps = true

on-window-detected = [
    # Optional Ghostty routing; remove this block if Ghostty is not installed.
    {
        if = 'test %{app-bundle-id} = com.mitchellh.ghostty',
        run = [
            'layout tiling',
            'move-node-to-workspace 1',
        ],
    },
    {
        if = 'test %{app-bundle-id} = com.microsoft.VSCode',
        run = 'move-node-to-workspace 2',
    },
    {
        if = 'test %{app-bundle-id} = com.apple.Safari',
        run = 'move-node-to-workspace 3',
    },
    {
        if = 'test %{app-bundle-id} = md.obsidian',
        run = 'move-node-to-workspace 5',
    },
    {
        if = 'test %{app-bundle-id} = com.hnc.Discord || test %{app-bundle-id} = ru.keepcoder.Telegram',
        run = 'move-node-to-workspace 6',
    },
    {
        if = 'test %{app-bundle-id} = com.spotify.client',
        run = 'move-node-to-workspace 7',
    },
]

[mode.main.binding]
cmd-alt-h = []

# Optional Ghostty launcher.
cmd-enter = 'exec-and-forget open -na Ghostty'

alt-slash = 'layout tiles horizontal vertical'
alt-comma = 'layout accordion horizontal vertical'

cmd-h = 'focus left'
cmd-j = 'focus down'
cmd-k = 'focus up'
cmd-l = 'focus right'

cmd-shift-h = 'move left'
cmd-shift-j = 'move down'
cmd-shift-k = 'move up'
cmd-shift-l = 'move right'

cmd-ctrl-m = 'focus-monitor --wrap-around next'
cmd-ctrl-shift-m = 'move-node-to-monitor --wrap-around --focus-follows-window next'

alt-minus = 'resize smart -50'
alt-equal = 'resize smart +50'

cmd-1 = 'workspace 1'
cmd-2 = 'workspace 2'
cmd-3 = 'workspace 3'
cmd-4 = 'workspace 4'
cmd-5 = 'workspace 5'
cmd-6 = 'workspace 6'
cmd-7 = 'workspace 7'
cmd-8 = 'workspace 8'
cmd-9 = 'workspace 9'

cmd-shift-1 = 'move-node-to-workspace 1'
cmd-shift-2 = 'move-node-to-workspace 2'
cmd-shift-3 = 'move-node-to-workspace 3'
cmd-shift-4 = 'move-node-to-workspace 4'
cmd-shift-5 = 'move-node-to-workspace 5'
cmd-shift-6 = 'move-node-to-workspace 6'
cmd-shift-7 = 'move-node-to-workspace 7'
cmd-shift-8 = 'move-node-to-workspace 8'
cmd-shift-9 = 'move-node-to-workspace 9'

alt-tab = 'workspace-back-and-forth'
cmd-p = 'move-workspace-to-monitor --wrap-around next'
cmd-shift-semicolon = 'mode service'

[mode.service.binding]
esc = ['reload-config', 'mode main']
r = ['flatten-workspace-tree', 'mode main']
f = ['layout floating tiling', 'mode main']
b = ['balance-sizes', 'mode main']
backspace = ['close-all-windows-but-current', 'mode main']
alt-shift-h = ['join-with left', 'mode main']
alt-shift-j = ['join-with down', 'mode main']
alt-shift-k = ['join-with up', 'mode main']
alt-shift-l = ['join-with right', 'mode main']

Reload it:

aerospace reload-config

5. Workspace layout

  • 1: Ghostty
  • 2: Visual Studio Code
  • 3: Safari
  • 4: spare workspace
  • 5: Obsidian
  • 6: Discord and Telegram
  • 7: Spotify
  • 8–9: spare workspaces

New app windows automatically move to their assigned workspace.

6. Main shortcuts

  • Command + 1…9: open workspace
  • Command + Shift + 1…9: move window to workspace
  • Command + H/J/K/L: focus left/down/up/right
  • Command + Shift + H/J/K/L: move window
  • Command + Control + M: switch focus between the two displays
  • Command + Control + Shift + M: move window to next display
  • Command + P: move current workspace to other display
  • Command + Enter: open Ghostty
  • Option + /: tiled layout
  • Option + ,: accordion layout
  • Option + Tab: previous workspace

7. Optional: Configure Swift Quit

Open Swift Quit and use:

  • Close delay: 2 seconds
  • Behaviour: quit every app except excluded apps
  • Launch at login: enabled

Exclude background or communication apps that should keep running, for example AeroSpace, Swift Quit, VPN clients, Discord, Telegram, Spotify, and Raycast.

Optional: Configure Ghostty splits

Native Ghostty tabs appear as separate macOS windows. Use internal splits instead.

Create ~/.config/ghostty/config:

keybind = super+t=new_split:auto
keybind = super+shift+[=goto_split:previous
keybind = super+shift+]=goto_split:next
keybind = ctrl+tab=goto_split:next
keybind = ctrl+shift+tab=goto_split:previous

Command + T now creates a split instead of a native tab. Press Command + Shift + , inside Ghostty to reload its config.

Fullscreen and terminal notes

I have not found a proper native-fullscreen fix with two displays yet, so I do not use it. I also avoid built-in Terminal and Ghostty native tabs; both conflict with AeroSpace. Ghostty splits work well.

Comments

No comments yet.

Leave a comment

Comments are checked by automated moderation before publication.