Skip to content

Layouts

Shoji ships with several built-in layouts and supports custom layouts. This page provides layout guidance and examples. For the complete list of configuration options, see Configuration overview.

Built-in layouts

LayoutDescription
tallMain window on left, stack on right
wideMain window on top, stack below
bspBinary space partitioning (recursive splits)
gridDynamic grid arrangement
columnEqual-width columns
fullscreenAll windows stacked full-size
floatingNo automatic tiling

Default layout

New macOS Spaces start with the default_layout:

spoon.Shoji:configure({
default_layout = "tall",
})

The default layout must appear in enabled_layouts.

Enabled layouts

Control which layouts are available when cycling (Ctrl+Alt+Space):

spoon.Shoji:configure({
enabled_layouts = { "tall", "wide", "fullscreen", "bsp" },
})

The cycle_layout action moves through this list in order. Layouts not in this list cannot be cycled to but can still be set directly via set_layout_* hotkeys.

Main area configuration

The tall and wide layouts split the screen into a main area and a stack. Two settings control this split:

Main ratio

The proportion of screen space for the main area:

spoon.Shoji:configure({
main_ratio = 0.5, -- Default: 50% main, 50% stack
})

Valid range: 0.1 to 0.9. Adjust at runtime with increase_main_ratio and decrease_main_ratio hotkeys (5% step).

main_ratio = 0.5:

┌─────────────────────────────┬─────────────────────────────┐
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ Main │ Stack │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
└─────────────────────────────┴─────────────────────────────┘

main_ratio = 0.7:

┌─────────────────────────────────────────┬─────────────────┐
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ Main │ Stack │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
└─────────────────────────────────────────┴─────────────────┘

Master window count

The number of windows in the main area:

spoon.Shoji:configure({
nmaster = 1, -- Default: 1 master window
})

Minimum: 1. Adjust at runtime with increase_nmaster and decrease_nmaster hotkeys.

nmaster = 1:

┌─────────────────────────────┬─────────────────────────────┐
│ │ │
│ │ │
│ │ │
│ │ Win 2 │
│ │ │
│ │ │
│ │ │
│ Win 1 ├─────────────────────────────┤
│ │ │
│ │ │
│ │ │
│ │ Win 3 │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
└─────────────────────────────┴─────────────────────────────┘

nmaster = 2:

┌─────────────────────────────┬─────────────────────────────┐
│ │ │
│ │ │
│ │ │
│ Win 1 │ Win 3 │
│ │ │
│ │ │
│ │ │
│ │ │
├─────────────────────────────┼─────────────────────────────┤
│ │ │
│ │ │
│ │ │
│ Win 2 │ Win 4 │
│ │ │
│ │ │
│ │ │
│ │ │
└─────────────────────────────┴─────────────────────────────┘

Custom layouts

Register custom layout implementations with the layouts option:

local MyLayout = require("my_custom_layout")
spoon.Shoji:configure({
layouts = { MyLayout },
enabled_layouts = { "tall", "mylayout" },
})

See Creating custom layouts for implementation details.

Layout modifiers

Modify built-in layouts with decorators. For example, mirror a layout:

local Modifiers = require("layouts.modifiers")
local Mirror = require("layouts.modifiers.mirror")
local Tall = require("layouts.tall")
local tallRight = Modifiers.wrap(Tall, Mirror.horizontal(), {
name = "tall-right",
displayName = "Tall (Right)",
})
spoon.Shoji:configure({
layouts = { tallRight },
enabled_layouts = { "tall", "tall-right", "wide" },
})

This creates a tall layout with the main area on the right instead of left. See Layout modifiers for all available modifiers.

Per-macOS Space layouts

Each macOS Space maintains its own layout. Changing layouts affects only the current macOS Space.

Bind hotkeys to switch directly to specific layouts:

spoon.Shoji:bindHotkeys({
set_layout_tall = { { "ctrl", "alt" }, "1" },
set_layout_wide = { { "ctrl", "alt" }, "2" },
set_layout_bsp = { { "ctrl", "alt" }, "3" },
})

Retile on focus

Some layouts (particularly those using the Magnifier modifier) benefit from retiling when focus changes:

spoon.Shoji:configure({
retile_on_focus = true,
})

This ensures focus-aware effects update immediately when switching windows. Leave it disabled for layouts that do not depend on focus state.