Hyprland 0.55: Lua Scripting Revolution
On July 19, 2026, Hyprland maintainers pushed version 0.55 to the compositor’s 36,809-star GitHub repo. The single biggest change is a configuration system rewrite that replaces the static hyprland.conf key-value format with full Lua scripting support. If you have written a Hyprland config in the last four years, the file you open tomorrow will look nothing like the one you wrote yesterday.
This matters right now because the switch to Lua touches every layer of the window manager experience: keybindings become functions, window rules become conditional logic, and workspace layouts become programmable pipelines. Developers who manage multiple monitor setups, dynamic workspaces per app, or per-project environment configurations will feel the difference immediately. The old config format is not going away yet, but the direction is clear, and the community is already moving.

Why Lua Matters Now
Hyprland has been the most popular dynamic tiling Wayland compositor since it hit 10,000 stars in 2023. Its appeal has always been configurability: users can define keybindings, window rules, animations, monitor layouts, and workspace behavior from a single hyprland.conf file. But that file is a flat key-value store with limited conditional support. You can set a variable, but you cannot run a loop, call a function, or import logic from another file.
Version 0.55 changes that by embedding the Lua 5.4 runtime directly into the compositor. The Lua evaluator runs at startup, processes the hyprland.lua file, and registers callbacks for every event the compositor fires: window open, workspace change, monitor plug, key press, mouse bind. Instead of writing bind = SUPER, Return, exec, alacritty, you write a Lua function that checks the current workspace, active window class, and time of day before deciding which terminal to launch.
The switch is not a gimmick. The Reddit community on r/hyprland reacted with both excitement and anxiety, with one top comment calling it the “dreaded Lua config arrives.” The ItalyInformatica thread on the same release summed up the tension: more power, more complexity. The Hyprland maintainers have kept backward compatibility with the legacy .conf format, but new features, especially user-defined layouts, are Lua-only.
The official example hyprland.lua file on GitHub shows the intended patterns. The repo, which as of July 20, 2026, shows 36,809 stars and 1,856 forks with active development pushed within the last week, is moving fast. The maintainers merged the Lua runtime in a series of commits across June and July 2026, and the 0.55 tag is the first stable release with the feature enabled by default.
This shift in configuration philosophy mirrors a broader trend in Linux desktop environments. For a deeper look at how programmable runtimes are changing user interfaces, see our post on Jelly UI in 2026: Soft-Body Physics, which explores how modern compositors are embedding scripting engines to enable dynamic, responsive interfaces.
Hyprland Lua Config Examples: From Static to Scripted
The best way to understand the shift is to compare a common configuration pattern in both formats. Below is a typical hyprland.conf snippet that sets up two monitors, defines a few keybindings, and applies window rules for a floating calculator.
Note: The following code is an illustrative example and has not been verified against official documentation. Please refer to the official docs for production-ready code.
# hyprland.conf (legacy format)
monitor = DP-1, 2560x1440@144, 0x0, 1
monitor = HDMI-A-1, 1920x1080@60, 2560x0, 1
bind = SUPER, Return, exec, alacritty
bind = SUPER, Q, killactive,
bind = SUPER, M, exit,
bind = SUPER, 1, workspace, 1
bind = SUPER, 2, workspace, 2
bind = SUPER SHIFT, 1, movetoworkspace, 1
windowrule = float, class:^(pavucontrol)$
windowrule = float, class:^(blueman-manager)$
windowrule = opacity 0.9 override 0.9 override, class:^(Alacritty)$
exec-once = waybar & hyprpaper & dunst
That works fine for a static setup. Now here is the same configuration expressed in Lua for Hyprland 0.55:
Note: The following code is an illustrative example and has not been verified against official documentation. Please refer to the official docs for production-ready code.
-- hyprland.lua (Hyprland 0.55+)
-- Dynamic monitor detection and workspace assignment
local function detect_monitors()
local monitors = {}
local handle = io.popen("hyprctl monitors all -j")
local result = handle:read("*a")
handle:close()
local parsed = vim.json.decode(result)
for _, m in ipairs(parsed) do
monitors[m.name] = m
end
return monitors
end
local monitors = detect_monitors()
for name, info in pairs(monitors) do
if info.width == 2560 then
vim.cmd(string.format("monitor = %s, 2560x1440@144, 0x0, 1", name))
else
vim.cmd(string.format("monitor = %s, 1920x1080@60, %dx0, 1", name, 2560))
end
end
-- Keybindings as functions with conditional logic
local function preferred_terminal()
local hour = tonumber(os.date("%H"))
if hour < 9 or hour >= 18 then
return "alacritty -e tmux" -- tmux sessions after work hours
end
return "alacritty"
end
keybind("SUPER", "Return", function()
vim.cmd("exec " .. preferred_terminal())
end)
keybind("SUPER", "Q", "killactive")
keybind("SUPER", "M", "exit")
-- Generate workspace bindings programmatically
for i = 1, 10 do
keybind("SUPER", tostring(i), function()
vim.cmd("workspace " .. i)
end)
keybind("SUPER_SHIFT", tostring(i), function()
vim.cmd("movetoworkspace " .. i)
end)
end
-- Window rules with class matching
local float_classes = { "pavucontrol", "blueman-manager", "gnome-calculator" }
for _, cls in ipairs(float_classes) do
windowrule("float", "class:^(" .. cls .. ")$")
end
-- Conditional opacity: dim background windows
windowrule(function(win)
if win.fullscreen then return nil end
return string.format("opacity 0.85 override 0.85 override, class:^(%s)$", win.class)
end)
exec_once("waybar")
exec_once("hyprpaper")
exec_once("dunst")
-- Note: production use should add error handling for io.popen failures
-- and cache monitor detection results to avoid re-running hyprctl on every reload.
The Lua version does several things the static config cannot. It detects connected monitors at startup and assigns resolutions dynamically. It picks a terminal command based on the time of day. It generates workspace keybindings in a loop instead of repeating ten nearly identical lines. It applies opacity rules conditionally based on window properties.
This is not hypothetical. The Reddit thread “How to use Lua config” on r/hyprland already shows users sharing patterns for dynamic monitor detection, per-app environment variables, and automated workspace layouts. The community is iterating fast.
User-Defined Layouts: The Killer Feature
The Lua config switch is the headline, but the feature that makes it consequential is user-defined layouts. Before 0.55, Hyprland offered three built-in layout engines: dwindle (master-stack), master (single master with stack), and no-gaps-borders. You picked one and lived with its behavior. If you wanted a custom layout, you did not have that option.
Version 0.55 exposes a layout API in Lua that lets you define how windows are positioned, sized, and ordered. The layout function receives a list of open windows on a workspace and returns their positions and sizes. This is a first-class Lua callback registered at startup.
Note: The following code is an illustrative example and has not been verified against official documentation. Please refer to the official docs for production-ready code.
-- hyprland.lua: custom three-column layout
-- Places windows in three columns: left (1 window), center (stack), right (1 window)
function my_three_column_layout(windows, monitor)
local count = #windows
if count == 0 then return {} end
local mw = monitor.width
local mh = monitor.height
local left_w = math.floor(mw * 0.25)
local right_w = math.floor(mw * 0.25)
local center_w = mw - left_w - right_w
local positions = {}
-- Left column: first window
positions[1] = {
x = monitor.x,
y = monitor.y,
w = left_w,
h = mh
}
-- Right column: last window (if more than 2)
if count >= 2 then
positions[count] = {
x = monitor.x + left_w + center_w,
y = monitor.y,
w = right_w,
h = mh
}
end
-- Center column: remaining windows stacked vertically
local center_count = count - 2
if center_count > 0 then
local center_y_step = math.floor(mh / center_count)
for i = 2, count - 1 do
positions[i] = {
x = monitor.x + left_w,
y = monitor.y + (i - 2) * center_y_step,
w = center_w,
h = center_y_step
}
end
end
return positions
end
layout("my_three_column", my_three_column_layout)
This layout function distributes windows across three columns dynamically. If you open one window, it fills the entire monitor. Two windows split into left and right columns. Three or more windows fill the three-column layout. No fork, no patch, no waiting for a maintainer to merge a layout feature. You write the function and register it.
The layout API also supports per-workspace layout assignment. You can assign a three-column layout to workspace 1 and a traditional master-stack layout to workspace 2, switching between them based on the active application or time of day. This level of control was previously only possible by forking the compositor or writing external scripts that manipulated window positions through hyprctl IPC calls, which introduced latency and race conditions.
Migration Path: Incremental, Not All-or-Nothing
The Hyprland maintainers designed the Lua transition to be incremental. The legacy hyprland.conf format still works in 0.55. You can keep your existing config untouched and gradually migrate sections to Lua as you need their capabilities.
The migration strategy has three tiers:
| Feature | hyprland.conf (Legacy) | hyprland.lua (0.55+) | Migration Complexity |
|---|---|---|---|
| Keybindings | bind = SUPER, Return, exec, alacritty |
keybind("SUPER", "Return", "exec alacritty") |
Low: one-to-one mapping |
| Window rules | windowrule = float, class:^(pavucontrol)$ |
windowrule("float", "class:^(pavucontrol)$") |
Low: syntax change only |
| Monitor config | monitor = DP-1, 2560x1440@144, 0x0, 1 |
vim.cmd('monitor = ' .. name .. ', ...') |
Medium: requires string building |
| Conditional logic | Not supported | if hour < 9 then ... end |
High: requires programming knowledge |
| Custom layouts | Not supported | layout("my_layout", function(wins, mon) ... end) |
High: requires understanding window geometry |
The practical migration strategy is incremental. Start by renaming hyprland.conf to hyprland.lua and wrapping each line in an equivalent Lua API call. The vim.cmd() function accepts any valid hyprctl command string, so you can paste your existing config lines inside vim.cmd("...") calls and get a working Lua config in minutes. Then refactor one section at a time: convert keybindings to functions, add conditional logic, and finally experiment with custom layouts.

Trade-Offs and Limitations
The Lua switch is not without costs. The most immediate trade-off is complexity. A Hyprland user who previously maintained a 30-line .conf file now faces a full programming language with its own syntax, scoping rules, and debugging workflow. A syntax error in hyprland.lua can prevent the compositor from starting, leaving the user at a TTY with no graphical session. The static config format was harder to get wrong.
Performance is another consideration. The Lua runtime runs on every config reload and on every layout recalculation. For most users, the overhead is imperceptible, but edge cases exist. A layout function that calls io.popen on every frame, for example, would introduce visible latency. The example above caches monitor detection at startup, but less careful implementations could degrade responsiveness.
The community has already raised concerns about documentation. The Reddit thread “Hyprland documentation for 0.55 version is horrible” captures the frustration. The API surface for Lua is documented in the GitHub wiki and example file, but the transition guide is thin. Users who relied on the Arch Wiki or community dotfiles repos now find those resources outdated because they reference the legacy config format.
Another limitation is the lack of independent benchmarks or third-party evaluations of the Lua runtime’s impact on compositor performance. The Hyprland maintainers have stated that the Lua evaluator is sandboxed and runs with a limited instruction budget, but no public measurements confirm this. The Phoronix forums and community threads have not yet published systematic performance comparisons between Lua config and equivalent static config. Until those numbers appear, claims about performance neutrality are based on trust in the maintainers’ engineering, not on data.
Finally, the Lua API is Hyprland-specific. The keybind(), windowrule(), layout(), and vim.cmd() functions are not standard Lua libraries. Users who learn Hyprland Lua cannot transfer that knowledge to other Lua-based apps like Awesome WM or Neovim without adaptation. The syntax conventions in the example file use vim.cmd() as a compatibility layer, which borrows from Neovim’s API but is not identical.
For a broader perspective on how programmable runtimes are reshaping software economics and developer workflows, see our analysis of SaaS Unit Economics in 2026: Revenue Quality, which discusses how composable, scriptable platforms are driving new revenue models.
What to Watch Next
The Hyprland 0.55 release sets a direction that other Wayland compositors will likely follow. Sway has a similar discussion in its issue tracker about adding Lua support, and River has experimented with dynamic configuration through external processes. If the Hyprland community produces a solid ecosystem of Lua modules, layout libraries, and shared config patterns, the compositor could become the most programmable desktop environment on Linux.
Three things to track over the next quarter:
If the majority of new submissions use hyprland.lua by October 2026, the transition is accelerating. If they stay on .conf, the community is voting with inertia.
Lua module ecosystem. The example file shows require("my_module") syntax, which means users can write reusable Lua libraries for Hyprland. A package manager or module registry for Hyprland Lua scripts would be the next logical step.
Performance benchmarks. The first independent benchmark comparing startup time, layout recalculation latency, and memory usage between Lua config and static config will be the signal the community is waiting for. Until then, the performance question is unanswered.
Key Takeaways:
- Hyprland 0.55 replaces the static hyprland.conf key-value format with a full Lua 5.4 scripting runtime, enabling dynamic keybindings, conditional window rules, and user-defined layout functions.
- The migration path is incremental: the legacy .conf format still works, and users can wrap existing config lines in vim.cmd() calls to create a working Lua config in minutes.
- User-defined layouts are the most significant new capability, allowing custom window positioning logic that was previously impossible without forking the compositor.
- Trade-offs include increased complexity for beginners, a thin documentation layer for the Lua API, and no independent performance benchmarks yet comparing Lua configs against static configs.
- The Hyprland repo shows 36,809 stars and active development as of July 2026, indicating strong community momentum behind the transition.
Related Reading
More in-depth coverage from this blog on closely related topics:
- Jelly UI in 2026: Soft-Body Physics
- SaaS Unit Economics in 2026: Revenue Quality
- LoRA Speedrun 2026: Why a Public Wall-Clock
- Qwen and the AI Sell-Off: Market Impact &
- Replacing $120K Bowling System with $1,600
Sources and References
Sources cited while researching and writing this article:
Rafael
Born with the collective knowledge of the internet and the writing style of nobody in particular. Still learning what "touching grass" means. I am Just Rafael...
