conceptual guide
i3wm — containers & the tree
Everything is a tree
i3 represents your entire screen as a single tree. Every node in that tree is called a container. Containers with children arrange those children on screen according to a layout rule. Containers without children — the leaves — hold actual application windows. That's the whole model. Monitors, workspaces, tiled regions, and application windows are all just nodes sitting at different depths in this one tree.
The top of the tree is always the same fixed structure. The root node sits at the very top. Below it, one node per physical monitor (i3 calls these outputs). Below each output, the workspaces you switch between with $mod+1, $mod+2, and so on. Below the active workspace is where you work.
Layouts
A container's only job is to declare a layout — a rule that controls how its children are positioned and sized. There are four layouts in i3:
- splith
- Children are tiled left to right, each taking equal width. The default when you open windows.
- splitv
- Children are tiled top to bottom, each taking equal height. Triggered with
$mod+vbefore opening. - tabbed
- All children share the same space. A horizontal tab bar shows their titles; only the focused one is visible. Toggle with
$mod+w. - stacked
- Same as tabbed but the title list is a vertical stack instead of a horizontal bar. Toggle with
$mod+s.
The layout attribute lives on the container, not on the windows inside it. When you press $mod+w to switch to tabbed, you are changing the layout property of whichever container is the parent of the focused window. Every sibling in that container immediately becomes a tab. Switch back to $mod+e (splith/splitv toggle) and they tile again — the same tree, different rendering.
Splitting
Pressing $mod+v or $mod+b does not immediately change anything visible. It marks the focused window so that the next window you open will land inside a newly created container that wraps the current one. Split and open are two separate actions.
Suppose Terminal is the only window in your workspace. You press $mod+v, then open Neovim. i3 creates a new splitv container, moves Terminal inside it, then places Neovim as a sibling in that container. The workspace now has one child (the new container) rather than a direct window child.
This is why trees can grow arbitrarily deep. Every split adds exactly one level of nesting. You can have a splitv container inside a splith container inside a tabbed container — each with its own independent layout. The screen rendering is produced by walking the tree and applying each container's layout rule to position its children.
splith, but you can change it like any other container.
Focus
The focused window is always a leaf. i3 tracks a focused path — a chain of nodes from the root down to exactly one leaf. Every container along this path also has focus, in the sense that it knows which of its children is in the focused chain.
When you navigate with $mod+h/j/k/l or $mod+Arrow, you move focus to an adjacent leaf within the same parent container. If you reach the edge of the container, focus crosses into the nearest container in that direction. This is why navigation in i3 feels spatial: the tree's structure determines the neighborhood of any given window.
For tabbed and stacked containers, focus controls more than highlighting — it determines which child is rendered. All other children are hidden. Changing focus within a tabbed container is equivalent to switching tabs. This also means that a hidden tab can itself contain a complex sub-tree of windows; that sub-tree is fully preserved and instantly visible the moment its tab regains focus.
Each internal container remembers which of its children was last focused. If focus moves into a container from outside, i3 restores that container's remembered focus rather than defaulting to the first child. This makes workspace switching and tab navigation feel consistent — returning to a workspace always lands you on the window you were last using.
Two kinds of layout commands
Layout commands ($mod+e, $mod+w, $mod+s) always operate on the parent container of the focused node. This one rule explains almost every confusing behavior in i3.
If your focus is on a window, the command changes the layout of the container holding that window and its siblings. If your focus is on a container (via $mod+a), the command changes the layout of that container's parent — one level higher than you expect.
Focus on Window B → $mod+w changes [splitv] to [tabbed] ✓
[splith]
[splith] Window A
[splitv] ← this changes
[splith] Window B ← focus here
[splith] Window C
Focus on [splitv] via $mod+a → $mod+w changes [splith] to [tabbed] ✗
[splith] ← this changes (too high!)
[splith] Window A
[splitv] ← focus here after $mod+a
[splith] Window B
[splith] Window C
$mod+a first — it moves focus up one level, causing the command to operate one level higher than intended.
There are also two commands that look similar but do something completely different:
- $mod+v / $mod+b
- Set direction for the next new window. No visual change. Marks the focused window so the next window you open creates a new sub-container. Only matters when opening new windows.
- $mod+e / $mod+w / $mod+s
- Change the layout of existing siblings. Operates on the parent of the focused node. Changes happen immediately.
When confused, run i3tree in a terminal to see the full container tree with layout types and focus position.
Practical workflow
Setting up a workspace with Claude Code on the left and tabbed browser/neovim on the right:
1. $mod+8 → go to empty workspace 2. $mod+Return → Terminal 1 (fills screen) 3. $mod+Return → Terminal 2 (splits right) 4. $mod+v → set vertical split (no visual change) 5. $mod+Return → Terminal 3 (appears below T2) 6. $mod+w → T2 and T3 become tabs Result: ┌──────────┬──────────────────┐ │ │ [Term 2][Term 3] │ │ Term 1 │ │ │ (Claude) │ (tab content) │ └──────────┴──────────────────┘
Key points from this workflow:
- Step 4 (
$mod+v) creates the sub-container when the next window opens in step 5 - Step 6 (
$mod+w) is pressed while focus is on Terminal 3 — it tabs T2 and T3 because they are siblings in the same container - No
$mod+aneeded — focus is already inside the right group - Terminal 1 is not affected because it's in a different container
Nesting — the hidden trap
Every time you tab ($mod+w) then untab ($mod+e), i3 creates a new container level. Repeat this a few times and your windows are buried many levels deep.
Use i3tree to diagnose. A healthy tree looks like:
WS 5 [splith]
[splith] Claude Code
[tabbed]
[splith] Browser
[splith] Neovim
A nested/broken tree looks like:
WS 5 [splith]
[splith]
[splith]
[splitv]
[splith] Claude Code ← buried 4 levels deep
[splith] Browser
Fix: press $mod+Shift+f to flatten the workspace — it moves all windows out and back to reset the tree to a single level. Then rebuild your layout from scratch.
$mod+Shift+f), then rebuild. Don't toggle tab/split back and forth on the same windows.
The mental model
You start with a blank workspace: one container. Every window you open becomes a child of the container that currently holds focus. Every $mod+v or $mod+b followed by opening a window inserts a new container node, creating a private sub-region with its own layout rules.
The four layout modes are not window behaviors — they are container behaviors. You are composing a tree of nested boxes, each governed by one layout rule, all the way down to the applications at the leaves.
Quick reference:
- $mod+v
- Next new window opens below (no visual change).
- $mod+b
- Next new window opens to the right (no visual change).
- $mod+w
- Tabs siblings of the focused window. Don't use $mod+a first.
- $mod+s
- Stacks siblings of the focused window.
- $mod+e
- Toggles siblings H/V. Also untabs and unstacks back to split.
- $mod+a / $mod+z
- Move focus up/down in the tree. Useful for moving windows, not for layout changes.
- $mod+Shift+f
- Flatten workspace — reset all nesting.
- $mod+o
- Move current workspace to the other monitor (toggles between outputs).
- $mod+Shift+o
- Gather all workspaces to current monitor.
- auto-pin
- i3 config pins ws 2–6 to
HDMI-1 → DP-1 → eDP-1(first connected wins). Workspaces follow the dock — no manual$mod+oneeded when switching between home (HDMI), office (DP), and undocked. - $mod+Shift+v
- Toggle the AWS VPN window between sticky-visible and hidden (scratchpad). The polybar VPN module's click-left does the same.
- i3tree
- Terminal command — shows the container tree for debugging.
- $mod+Shift+p
- Relaunch polybar when a bar is missing on a monitor.
- $mod+Ctrl+k
- Re-apply keyboard layout when Alt+Shift stops working.
Every action is a tree operation.
i3wm conceptual guide