Overview
You will learn
- How to build layouts from scratch
- How to add configurable features to layouts
- How to use modifiers and combinators for composition
This section teaches you to build custom layouts for Shoji. By the end, you’ll understand how layouts work and have the skills to create your own.
Which tutorial should I start with?
- “I want to customize my setup” — You probably don’t need a tutorial. Check the Recipes for copy-paste configurations you can adapt.
- “I want to build my own layout” — Start with Part 1. It walks you through the layout protocol from scratch.
- “I want to understand composition” — Jump to Part 3 if you already know the basics and want to learn modifiers and combinators.
Prerequisites
Before starting:
- Shoji installed and running (Installation)
- Basic Lua knowledge (variables, functions, tables)
- A text editor for writing Lua files
The tutorials
Part 1: Your first layout
Build a simple column layout from scratch. You’ll learn the layout protocol and how Shoji uses layouts to position windows.
What you’ll build: A layout that arranges windows in equal vertical columns.
Time: Follow along at your own pace.
Part 2: Making it configurable
Extend your layout to respond to user actions. Add capabilities that let users adjust the main ratio and number of main windows.
What you’ll build: A main-stack layout with adjustable regions.
Prerequisites: Complete Part 1.
Part 3: Modifiers and composition
Transform existing layouts without rewriting them. Use modifiers and combinators to create layout variations.
What you’ll build: Custom modifiers and composed layouts.
Prerequisites: Complete Part 2.
Reference material
After completing the tutorials, use these references (also in this section):
- Layout protocol — Full protocol specification
- Modifiers — Built-in modifiers and chaining
- Partition — Divide the screen into regions with different layouts
- IfMax — Switch between layouts based on window count
Debugging layouts
When a layout doesn’t behave as expected, these techniques help you find the problem.
Check the Hammerspoon console. Open it from the menu bar (Console) or press Cmd+Alt+C. Error messages appear here with file names and line numbers. Common errors include:
attempt to index a nil value— a parameter or table field you expected isn’t there. Check yourarrange()function’sparamstable.attempt to perform arithmetic on a nil value— you’re using a missing number (oftenmainRatioormainCountbefore declaring them as capabilities).
Add print() in arrange(). Temporarily print params and
computed frames to verify your geometry math:
function MyLayout.arrange(params) print("workarea:", hs.inspect(params.workarea)) print("window count:", #params.windowIDs) -- your layout logic ...endUse hooks to observe layout execution. The before_tile
and after_tile hooks let you inspect what Shoji sends to
your layout and what frames result:
spoon.Shoji:on("before_tile", function(spaceID) print("tiling space " .. spaceID)end)See Part 1 for common errors and Part 3 for a debug modifier pattern that logs frames without changing them.
Testing your work
Throughout these tutorials, you’ll test layouts in Hammerspoon:
- Save your layout file
- Reload Hammerspoon from the menu bar (Reload config) or run
hs.reload()in the Hammerspoon console - Cycle to your layout (Ctrl+Alt+Space)
- Check the console for errors (menu bar > Console)
Keep the console open while developing to catch errors early.