instantWM Configuration
instantWM is customized through a TOML configuration file at ~/.config/instantwm/config.toml.
After editing the config file, you can either restart instantWM or use instantwmctl to apply changes dynamically for certain settings.
Configuration File Location
~/.config/instantwm/config.tomlIf the file doesn't exist, instantWM will use sensible defaults.
Config Includes
You can split your configuration into multiple files using the includes directive:
# config.toml (main file)
[[includes]]
file = "keybinds.toml"
[[includes]]
file = "colors.toml"Included files are merged into the main configuration. Paths can be absolute or relative to the main config file. Circular includes are detected and prevented.
Themes
instantWM ships with several built-in colour themes that style the status bar, tags, window titles, borders and close buttons together. Set the top-level theme key to pick one:
theme = "nord"That single line replaces the entire [colors] table — every colour is derived from the theme's palette. Themes are resolved when the config is read, so run instantwmctl reload (or restart instantWM) after changing theme.
Built-in themes
| Name | Description |
|---|---|
instantos | The default theme. Dark background with blue, green, yellow and red accents. |
catppuccin-latte | Light pastel theme (Catppuccin Latte). |
catppuccin-frappe | Dark, muted theme (Catppuccin Frappé). |
catppuccin-macchiato | Dark theme (Catppuccin Macchiato). |
catppuccin-mocha | Dark theme (Catppuccin Mocha). |
nord | Dark, cool-blue theme (Nord). |
gruvbox | Dark, warm earth-tone theme (Gruvbox). |
Overriding theme colours
A theme is only the base. Anything you set under [colors] is merged on top of the selected theme, so you can keep a theme and tweak individual colours rather than redefining the whole palette:
theme = "catppuccin-mocha"
# Keep the whole Mocha palette, but make the focused tiled-window
# border pink instead of the theme's blue.
[colors.border]
tile_focus = "#f5c2e7"See Color Schemes for every key you can override. Because a theme resolves to the same [colors] structure, you can also keep a theme choice (and any overrides) in its own file and pull it in with includes.
INFO
An unknown theme name is not fatal. instantWM prints a warning and falls back to the default theme (instantos) — the rest of your config still loads normally.
Switching themes at runtime
You can switch themes on the running WM without editing the config:
instantwmctl theme # print the active theme
instantwmctl theme nord # switch to a theme
instantwmctl theme --list # list available themesThe bar, borders and tags recolour immediately. This is a runtime change only — instantwmctl reload reverts to whatever theme is set to in config.toml. To make a theme permanent, set theme = "..." in the config.
Full Configuration Example
# Fonts - first font is primary, others are fallbacks
fonts = ["Cantarell-Regular:size=12", "JetBrains Mono:size=11"]
# Color configuration
[colors.tag.normal]
inactive = { fg = "#DFDFDF", bg = "#121212", detail = "#121212" }
filled = { fg = "#DFDFDF", bg = "#384252", detail = "#89B3F7" }
focus = { fg = "#121212", bg = "#89B3F7", detail = "#89B3F7" }
nofocus = { fg = "#DFDFDF", bg = "#292F3A", detail = "#3E485B" }
empty = { fg = "#5E6572", bg = "#121212", detail = "#121212" }
[colors.tag.hover]
inactive = { fg = "#DFDFDF", bg = "#1E2229", detail = "#1E2229" }
filled = { fg = "#DFDFDF", bg = "#4A5568", detail = "#A7BDD9" }
focus = { fg = "#121212", bg = "#A7BDD9", detail = "#A7BDD9" }
nofocus = { fg = "#DFDFDF", bg = "#353D4B", detail = "#475166" }
empty = { fg = "#6E7889", bg = "#1E2229", detail = "#1E2229" }
[colors.window.normal]
focus = { fg = "#DFDFDF", bg = "#292F3A", detail = "#3E485B" }
normal = { fg = "#6E7889", bg = "#292F3A", detail = "#3E485B" }
minimized = { fg = "#6E7889", bg = "#121212", detail = "#121212" }
sticky = { fg = "#F9D71C", bg = "#292F3A", detail = "#3E485B" }
sticky_focus = { fg = "#121212", bg = "#F9D71C", detail = "#F9D71C" }
edge_scratchpad = { fg = "#89B3F7", bg = "#292F3A", detail = "#3E485B" }
edge_scratchpad_focus = { fg = "#121212", bg = "#89B3F7", detail = "#89B3F7" }
[colors.window.hover]
focus = { fg = "#DFDFDF", bg = "#353D4B", detail = "#475166" }
normal = { fg = "#7E8899", bg = "#353D4B", detail = "#475166" }
minimized = { fg = "#7E7889", bg = "#1E2229", detail = "#1E2229" }
sticky = { fg = "#F9D71C", bg = "#353D4B", detail = "#475166" }
sticky_focus = { fg = "#121212", bg = "#F9D71C", detail = "#F9D71C" }
edge_scratchpad = { fg = "#89B3F7", bg = "#353D4B", detail = "#475166" }
edge_scratchpad_focus = { fg = "#121212", bg = "#89B3F7", detail = "#89B3F7" }
[colors.close_button.normal]
normal = { fg = "#6E7889", bg = "#292F3A", detail = "#3E485B" }
locked = { fg = "#6E7889", bg = "#292F3A", detail = "#3E485B" }
fullscreen = { fg = "#F9D71C", bg = "#292F3A", detail = "#3E485B" }
[colors.close_button.hover]
normal = { fg = "#DFDFDF", bg = "#81C995", detail = "#5EA984" }
locked = { fg = "#DFDFDF", bg = "#E16A98", detail = "#B7416E" }
fullscreen = { fg = "#121212", bg = "#F9D71C", detail = "#D4A61A" }
[colors.border]
normal = "#384252"
tile_focus = "#89B3F7"
float_focus = "#81C995"
snap = "#FDD663"
[colors.status]
fg = "#DFDFDF"
bg = "#121212"
detail = "#3E485B"
# Keyboard layout configuration
[keyboard]
layouts = [
{ name = "us" },
{ name = "de", variant = "nodeadkeys" }
]
options = "compose:ralt"
# Input configuration (touchpad, mouse, etc.)
[input]
# Example: enable tap-to-click on touchpads
# [input."type:touchpad"]
# tap = "enabled"
# natural_scroll = "enabled"
# Custom keybinds
[[keybinds]]
modifiers = ["Super"]
key = "Return"
action = { spawn = ["alacritty"] }
[[keybinds]]
modifiers = ["Super", "Shift"]
key = "q"
action = "kill"
[[keybinds]]
modifiers = ["Super"]
key = "F3"
action = "next_keyboard_layout"
# Desktop keybinds (work without a focused window)
[[desktop_keybinds]]
modifiers = ["Super"]
key = "d"
action = { spawn = ["instantmenu"] }Color Schemes
Each color property has normal and hover variants that switch when hovered with the mouse.
Color Types
Each element has three colors:
- fg (foreground): text color
- bg (background): behind the text
- detail: shading details below the element
Element Types
| Element | Description |
|---|---|
| tag | Tag number indicator |
| window | Window title |
| close_button | Close button on active window title |
| border | Window border colors |
| status | Status bar text |
Color States
Tags:
inactive: Tag not selected, no windowsfilled: Tag has windows but none focusedfocus: Tag selected and has focused windownofocus: Tag not selected but has windowsempty: Tag selected but no windows
Windows:
focus: Currently focused windownormal: Regular unfocused windowminimized: Minimized windowsticky: Sticky window (visible on all tags)sticky_focus: Sticky window that is focusededge_scratchpad: Window assigned to the edge overlayedge_scratchpad_focus: Focused edge-overlay window
Close Button:
normal: Default close buttonlocked: Locked window close buttonfullscreen: Fullscreen window close button
Border:
normal: Unfocused window bordertile_focus: Focused tiled window borderfloat_focus: Focused floating window bordersnap: Snapped window border
Keyboard Configuration
The [keyboard] section configures XKB keyboard layouts:
[keyboard]
layouts = [
{ name = "us" },
{ name = "de", variant = "nodeadkeys" },
{ name = "fr" }
]
options = "compose:ralt" # XKB options
model = "pc105" # Keyboard model (optional)
swapescape = false # Swap Caps Lock and EscapeSet swapescape = true to swap Caps Lock and Escape without having to spell out the XKB option string yourself.
Input Configuration
Configure touchpad and mouse settings:
# Global settings
[input]
[input."type:touchpad"]
tap = "enabled"
natural_scroll = "enabled"
accel_profile = "adaptive"
pointer_accel = 0.5
[input."type:mouse"]
pointer_accel = 0.3Valid values:
tap: "enabled" or "disabled"natural_scroll: "enabled" or "disabled"accel_profile: "flat" or "adaptive"pointer_accel: Floating point numberscroll_factor: Floating point multiplier applied to scroll events (defaults to1.0when unset)left_handed: "enabled" or "disabled" — swaps the primary/secondary buttons for left-handed use
Layout tree and gaps
The layout section controls spacing and interaction with instantWM's persistent manual tree:
[layout]
inner_gap = 8
outer_gap = 8
smart_gaps = true
maximized_gaps = false
keyboard_resize_step = 0.05
minimum_weight = 0.15
pointer_edge_fraction = 0.34
new_window_placement = "auto-resize"| Setting | Type | Default | Description |
|---|---|---|---|
inner_gap | integer | 0 | Spacing between tiled windows (logical pixels) |
outer_gap | integer | 0 | Spacing between tiled windows and the monitor edge (logical pixels) |
smart_gaps | boolean | false | Disable all gaps when only one or zero tiled windows are present |
maximized_gaps | boolean | false | Apply configured gaps during maximized presentation |
keyboard_resize_step | float | 0.05 | Fraction of an axis transferred by one tree resize command |
minimum_weight | float | 0.15 | Preferred minimum weight of a child in a split run |
pointer_edge_fraction | float | 0.34 | Fraction of a target occupied by pointer placement edge bands |
new_window_placement | string | "auto-resize" | How a new window joins the tiling tree (see below) |
new_window_placement controls where a window that is not yet in a tag's persistent tiling tree gets inserted:
"auto-resize"(default) — place the newcomer automatically and resize the existing tree to give it room."auto"— split the best existing leaf without deliberately rebalancing the rest of the tree."force"— give the first newcomer a leading half of a new vertical root split; consecutive untouched insertions adapt that region into balanced rows or columns, and any manual tree edit starts a new sequence.
Inner gaps are split evenly between adjacent windows. Outer gaps shrink the layout area inward from all four edges. Both values are clamped to a minimum of 0.
Floating windows are not affected.
Layout presets such as Grid are one-shot tree rewrites. These settings govern the manual edits which remain afterward; see Layouts.
Floating windows and click-to-raise
# Raise a floating window to the top of the stack when its client area is
# left-clicked. Disabled by default so click-to-focus and focus-follows-mouse
# do not disturb the explicit floating-window stacking order.
raise_floating_on_click = falseWith the default (false), clicking inside a floating window focuses it without changing the stacking order, which keeps manually arranged floating windows where you put them. Set it to true if you prefer each click to also bring the window to the front.
Animation speed
[animations]
# 1.0 = designed speed; 0.5 = half speed (durations doubled);
# 2.0 = twice as fast (durations halved)
speed = 1.0| Setting | Type | Default | Description |
|---|---|---|---|
speed | float | 1.0 | Global animation speed multiplier; valid range 0.01–100.0 |
Below 1.0 slows animations down, above 1.0 speeds them up. Because durations are scaled, a factor of 2.0 does not skip frames — it halves every animation's duration. Use instantwmctl toggle animated (or super+shift+alt+s) to disable animations entirely; the speed multiplier then has no effect until animations are re-enabled.
The speed can also be changed at runtime:
instantwmctl config get animations.speed
instantwmctl config set animations.speed 1.5instantwmctl config set applies immediately but is not persisted — keep the value in config.toml for a permanent setting.
Custom Keybinds
Add or override keybinds:
# Spawn a command
[[keybinds]]
modifiers = ["Super"]
key = "Return"
action = { spawn = ["alacritty"] }
# Named actions
[[keybinds]]
modifiers = ["Super", "Shift"]
key = "q"
action = "kill"
# Remove a default binding
[[keybinds]]
modifiers = ["Super"]
key = "f"
action = { unbind = true }
# Apply a tree preset
[[keybinds]]
modifiers = ["Super"]
key = "g"
action = { set_layout = "grid" }
# Adjust master window count
[[keybinds]]
modifiers = ["Super"]
key = "i"
action = { inc_master_count = 1 }
# Enter a mode
[[keybinds]]
modifiers = ["Super"]
key = "r"
action = { set_mode = "resize" }Available Modifiers
SuperorMod4- Windows/Super keyShift- Shift keyCtrlorControl- Control keyAltorMod1- Alt key
Available actions
Simple actions use a string, for example action = "begin_tree_placement" or action = "toggle_tiling_maximized". The most relevant tree actions are:
focus_left/right/up/downkey_move_left/right/up/downkey_resize_left/right/up/downtree_growandtree_shrinkbegin_tree_placementlayout_tile,layout_grid,layout_horiz_grid,layout_bottom_stack, andlayout_bstack_horizlayout_float,layout_maximized, andtoggle_tiling_maximizededge_scratchpad_createandedge_scratchpad_toggle
Do not copy a static action catalog from the web and assume it matches a custom build. instantwm --list-actions or instantwmctl action --list prints the authoritative, parser-backed list with descriptions and argument examples.
Structured actions accepted in TOML are:
spawn:action = { spawn = ["alacritty"] }unbind:action = { unbind = true }none: useaction = "none"to remove a binding (equivalent tounbind)set_layout:action = { set_layout = "tile" }focus_stack:action = { focus_stack = "next" }inc_master_count:action = { inc_master_count = 1 }keyboard_layout:action = { keyboard_layout = "us(intl)" }set_mode:action = { set_mode = "resize" }
See Modes for mode-local bindings and the built-in placement mode.
Window rules
Window rules apply placement and tag settings automatically when a window matching a given class, instance, or title appears. A window matches a rule when every criterion you supply matches; criteria you leave out match anything. class, instance, and title are case-sensitive substring matches.
# Centre pavucontrol as a floating window
[[rules]]
class = "pavucontrol"
is_floating = "float_center"
# Open mpv floating on tag 3 (bit 2 → value 4)
[[rules]]
class = "mpv"
is_floating = "float"
tags = 4
# Match on the window title
[[rules]]
class = "steam"
title = "Friends List"
is_floating = "float"
# Pin a game to fullscreen on the second monitor
[[rules]]
class = "complex-game"
is_floating = "float_fullscreen"
monitor = { index = 1 }| Field | Description |
|---|---|
class | Match the window's WM class (substring). |
instance | Match the window's WM instance (substring). |
title | Match the window title (substring). |
tags | Bitmask of tags to assign. Bit 0 (value 1) is tag 1, bit 1 (value 2) is tag 2, bit 2 (value 4) is tag 3, and so on. Combine tags with bitwise OR (tags 1 + 3 = `1 |
is_floating | Initial mode: "tiled", "float", "float_center", "float_fullscreen", or "scratchpad". |
monitor | "any" (default) or { index = N } for a specific monitor by 0-based index. |
Only the first matching rule is applied to a window.
Control Commands
Runtime control is provided by instantwmctl. See the canonical instantwmctl command reference for commands and examples. For actions usable in keybindings, instantwmctl action --list is the current runtime-generated list.
Runtime Control
instantWM provides the instantwmctl command-line tool for runtime control. See the instantwmctl documentation for a complete reference.
instantWM reads status text from the X11 root window name property (X11) or writes to the status bar directly (Wayland). Configure a status command in your config:
status_command = "i3status-rs"Or set status manually:
instantwmctl update-status "My Status"Custom modes
Modes use the same binding format and can invoke the tree actions above. See Modes for a complete, current example and for customizing the built-in placement mode.
Startup commands
Like sway's exec / exec_once and Hyprland's exec-once, you can run commands when instantWM starts:
# Run once at startup (not repeated on reload)
exec_once = ["xmobar", "wal -R"]
# Run at startup and again on every `instantwmctl reload`
exec = ["killall picom; picom"]exec_once is only fired during the initial startup sequence; exec is also re-run on each config reload, so it suits commands that need to be kept alive or restarted when the config changes.
Monitor Configuration
Configure specific monitor settings:
[monitors."DP-1"]
resolution = "1920x1080"
refresh_rate = 144.0
position = "0,0"
scale = 1.0
enable = true
transform = "normal" # rotation / reflection
vrr = "auto" # variable refresh rate policy
[monitors."HDMI-A-1"]
position = "left-of:DP-1"Position can be specified as:
- Absolute:
"X,Y"(e.g.,"1920,0") - Relative:
"left-of:OUTPUT","right-of:OUTPUT","above:OUTPUT","below:OUTPUT"
transform rotates or mirrors the output. Valid values are "normal", "90", "180", "270", "flipped", "flipped-90", "flipped-180", and "flipped-270".
vrr controls variable refresh rate (FreeSync / G-Sync) and accepts "off", "auto" (default — let the driver decide), or "on".
Bar height
# Bar height in logical pixels. 0 = derive from the configured fonts.
bar_height = 0By default (0) the bar height is derived from the font metrics of the configured fonts. Set a fixed pixel height if you want the bar to stay a specific size regardless of font choice.
Cursor (Wayland)
On the Wayland backend, the cursor theme and size are taken from the [cursor] section (this has no effect on X11):
[cursor]
theme = "Adwaita" # xcursor theme name
size = 24 # cursor size in logical pixelsThese mirror the XCURSOR_THEME and XCURSOR_SIZE environment variables; the config values take precedence. Reload the config with instantwmctl reload to apply a change.