Skip to content

Binds

Note

Looking for the old hyprlang syntax? Check the 0.54 wiki pages. Since Hyprland 0.55, hyprlang is deprecated in favor of lua.

Basic

hl.bind(keys, dispatcher)

for example,

hl.bind("SUPER + SHIFT + Q", hl.dsp.exec_cmd("firefox"))

will bind opening Firefox to SUPER + SHIFT + Q

Note

For binding keys without a modkey, leave it empty:

hl.bind("Print", hl.dsp.exec_cmd("grim"))

The dispatcher list can be found in Dispatchers.

You can also put a lua function if you prefer as your bind dispatcher:

hl.bind("SUPER + SHIFT + X", function()
    -- some logic...
    hl.dispatch(hl.dsp.window.float({ action = "toggle" }))
end)

Uncommon syms / binding with a keycode

See the xkbcommon-keysyms.h header for all the keysyms. The name you should use is the segment after XKB_KEY_.

If you want to bind by a keycode, you can put it in the KEY position with a code: prefix, e.g.:

hl.bind("SUPER + code:28", hl.dsp.exec_cmd("amongus"))

This will bind SUPER + t since t is keycode 28.

Note

If you are unsure of what your key’s name or keycode is, you can use wev to find out.

Bind flags

hl.bind() supports flags in this format:

hl.bind(keys, dispatcher, { flag1 = true, flag2 = true })

e.g.:

hl.bind(keys, hl.dsp.exec_cmd("amongus"), { release = true, locked = true })

Available flags:

FlagDescription
lockedWill also work when an input inhibitor (e.g. a lockscreen) is active.
releaseWill trigger on release of a key.
clickWill trigger on release of a key or button as long as the mouse cursor stays inside binds:drag_threshold.
dragWill trigger on release of a key or button as long as the mouse cursor moves outside binds:drag_threshold.
long_pressWill trigger on long press of a key.
repeatingWill repeat when held.
non_consumingKey/mouse events will be passed to the active window in addition to triggering the dispatcher.
mouseSee the dedicated Mouse Binds section.
transparentCannot be shadowed by other binds.
ignore_modsWill ignore modifiers.
separateWill arbitrarily combine keys between each mod/key, see Keysym combos.
descriptionWill allow you to write a description for your bind.
bypassBypasses the app’s requests to inhibit keybinds.
submap_universalWill be active no matter the submap.
devicesAllow binds to be set per device. See Per-Device Binds

Example Usage:

-- Example volume button that allows press and hold, volume limited to 150%
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+"), { repeating = true })

-- Example volume button that will activate even while an input inhibitor is active
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), { locked = true } )

-- Open wofi on first press, closes it on second
hl.bind("SUPER + SUPER_L", hl.dsp.exec_cmd("pkill wofi || wofi"), { release = true })

-- Skip player on long press and only skip 5s on normal press
hl.bind("SUPER + XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { long_press = true })
hl.bind("SUPER + XF86AudioNext", hl.dsp.exec_cmd("playerctl position +5"))

Mouse buttons

You can also bind or unbind mouse buttons by prefacing the mouse keycode with mouse:, e.g.:

hl.bind("SUPER + mouse:272", hl.dsp.exec_cmd("amongus"))  -- bind `exec amogus` to SUPER + LMB.

Binding modkeys only

To only bind modkeys, you need to use the TARGET modmask (with the activating mod) and the r flag, e.g.:

-- bind `exec amongus` to SUPER + ALT.
hl.bind("ALT + ALT_L", hl.dsp.exec_cmd("amongus"), { release = true })

Mouse wheel

You can also bind mouse wheel events with mouse_up and mouse_down (or mouse_left and mouse_right if your mouse supports horizontal scrolling):

hl.bind("SUPER + mouse_down", hl.dsp.focus({ workspace = "e-1" }))

Note

You can control the reset time with hl.config.binds.scroll_event_delay.

Switches

Switches are useful for binding events like closing and opening a laptop’s lid:

-- Trigger when the switch is toggled.
hl.bind("switch:[switch name]", hl.dsp.exec_cmd("swaylock"), { locked = true })
-- Trigger when the switch is turning on.
hl.bind("switch:on:[switch name]", hl.dsp.exec_cmd("notify-send 'yooo'"), { locked = true })
-- Trigger when the switch is turning off.
hl.bind("switch:off:[switch name]", hl.dsp.exec_cmd("notify-send 'among us'"), { locked = true })

Warning

Systemd HandleLidSwitch settings in logind.conf may conflict with Hyprland’s laptop lid switch configurations.

Note

You can view your switches with hyprctl devices.

Multiple binds to one key

You can trigger multiple actions with the same keybind by using a lua lambda function, with different disapatchers and params:

-- To switch between windows in a floating workspace:
hl.bind("SUPER + Tab", function()
    hl.dispatch(hl.dsp.window.cycle_next())    -- Change focus to another window
    hl.dispatch(hl.dsp.window.bring_to_top()) -- Bring it to the top
end)

Warning

The keybinds will be executed top to bottom, in the order they were written in.

Description

You can describe your keybind with the description flag.
Your description always goes in the flags section.

hl.bind(keys, dispatcher, { description = "your description here"})

For example:

hl.bind("SUPER + Q", hl.dsp.exec_cmd("kitty"), { description = "Open my favourite terminal" })

If you want to access your description you can use hyprctl binds.
For more information have a look at Using Hyprctl.

Per-Device Binds

You can set keybinds to be device specific with the devices flag.
Devices are provided in a whitespace separated list that goes in front of dispatcher.
An ! can be prepended to the list to exclude the those devices, allowing all other devices to use that bind instead.

hl.bind(keys, dispatcher(params), { device = { inclusive = true, list = { "device1", "device1" } } })
-- Only example-keyboard-1 can use this bind
hl.bind("SUPER + Q", hl.dsp.exec_cmd("kitty"), { devices = { inclusive = true, list = { "example-keyboard-1" } } })

-- Every keyboard other than razer-keyboard and asus-keyboard can use this bind
hl.bind("SUPER + Q", hl.dsp.exec_cmd("kitty"), { devices = { inclusive = false, list = { "razer-keyboard", "asus-keyboard" } } })

You can check device names with hyprctl devices.

Mouse Binds

These are binds that rely on mouse movement. They will have one less arg.
hl.config.binds.drag_threshold can be used to differentiate between clicks and drags with the same button:

hl.config({
    binds {
        drag_threshold = 10 -- Fire a drag event only after dragging for more than 10px
    }
})
hl.bind("ALT + mouse:272", hl.dsp.window.drag(), { mouse = true })    -- ALT + LMB: Move a window by dragging more than 10px.
hl.bind("ALT + mouse:272", hl.dsp.window.resize(), { mouse = true })  -- ALT + LMB: Floats a window by clicking

Available mouse binds:

NameDescriptionParams
drag()moves the active windowNone
resize()resizes the active windowNone

Common mouse button key codes (check wev for other buttons):

LMB -> 272
RMB -> 273
MMB -> 274

Note

Mouse binds, despite their name, behave like normal binds.
You are free to use whatever keys / mods you please. When held, the mouse function will be activated.

Touchpad

As clicking and moving the mouse on a touchpad is unergonomic, you can also use keyboard keys instead of mouse clicks.

hl.bind("SUPER + mouse:272", hl.dsp.window.drag(), { mouse = true })
hl.bind("SUPER + CTRL_L", hl.dsp.window.drag(), { mouse = true })

hl.bind("SUPER + mouse:273", hl.dsp.window.resize(), { mouse = true })
hl.bind("SUPER + ALT_L", hl.dsp.window.resize(), { mouse = true })

Global Keybinds

Classic

Yes, you heard this right, Hyprland does support global keybinds for ALL apps, including OBS, Discord, Firefox, etc.

See the pass and sendshortcut dispatchers for keybinds.

Let’s take OBS as an example: the “Start/Stop Recording” keybind is set to SUPER + F10, to make it work globally, simply add:

hl.bind("SUPER + F10", hl.dsp.pass("class:^(com\.obsproject\.Studio)$"))

to your config and you’re done.

pass will pass the PRESS and RELEASE events by itself, no need for a bindr.
This also means that push-to-talk will work flawlessly with one pass, e.g.:

hl.bind("mouse:276", hl.dsp.pass("class:^(TeamSpeak 3)$"))    # Pass MOUSE5 to TeamSpeak3.

You may also add shortcuts, where other keys are passed to the window.

hl.bind("SUPER + F10", hl.dsp.send_shortcut({ mods = "SUPER", key = "F4", window = "class:^(TeamSpeak 3)$" }))  -- Send SUPER + F4 to OBS when SUPER + F10 is pressed.

Warning

This works flawlessly with all native Wayland applications, however, XWayland is a bit wonky.
Make sure that what you’re passing is a “global Xorg keybind”, otherwise passing from a different XWayland app may not work.

DBus Global Shortcuts

Some applications may already support the GlobalShortcuts portal in xdg-desktop-portal.
If that’s the case, it’s recommended to use the following method instead of pass:

Open your desired app and run hyprctl globalshortcuts in a terminal.
This will give you a list of currently registered shortcuts with their description(s).

Choose whichever you like, for example coolApp:myToggle, and bind it to whatever you want with the global dispatcher:

hl.bind("SUPER + SHIFT + A", hl.dsp.global("coolApp:myToggle"))

Note

Please note that this function will only work with XDPH.

Submaps

Keybind submaps allow you to activate aseparate set of keybinds.
For example, if you want to enter a resize mode that allows you to resize windows with the arrow keys, you can do it like this:

-- Switch to a submap called `resize`.
hl.bind("ALT + R", hl.dsp.submap("resize"))

-- Start a submap called "resize".
hl.define_submap("resize", function()

    -- Set repeating binds for resizing the active window.
    hl.bind("right", hl.resize({ x = 10, y = 0, relative = true}), { repeating = true })
    hl.bind("left", hl.resize({ x = -10, y = 0, relative = true}), { repeating = true })
    hl.bind("up", hl.resize({ x = 0, y = 10, relative = true}), { repeating = true })
    hl.bind("down", hl.resize({ x = 10, y = -10, relative = true}), { repeating = true })

    -- Use `reset` to go back to the global submap
    hl.bind("escape", hl.dsp.submap("reset"))

end)

-- Keybinds further down will be global again...

Warning

Do not forget a keybind (escape, in this case) to reset the keymap while inside it!

If you get stuck inside a keymap, you can use hyprctl dispatch 'hl.dsp.submap("reset")' to go back.
If you do not have a terminal open, open a new tty and use the –instance flag to select which instanceof hyprland to operate on (if you only have one running this is 0). For example: hyprctl dispatch --instace 0 'hl.dsp.submap("reset")'

You can also set the same keybind to perform multiple actions, such as resize and close the submap, like so:

hl.bind("ALT + R", hl.dsp.submap("resize"))

hl.define_submap("resize", function()
    hl.bind("right", function()
        hl.window.resize({ x = 10, y = 0, relative = true })
        hl.dsp.submap("reset")
    end)
end)

This works because the binds are executed in the order they appear, and assigning multiple actions per bind is possible.

You can set a keybind that will be active no matter the current submap with the submap universal bind flag.

hl.bind(mainMod .. " + K", hl.dsp.exec_cmd("kitty"), { submap_universal = true })

Nesting

Submaps can be nested, see the following example:

hl.bind(mainMod .. " + M", hl.dsp.submap("main_submap"))
hl.define_submap(main_submap, function()

    -- ...

    -- nested_one
    hl.bind("1", hl.dsp.submap("nested_one"))
    hl.define_submap("nested_one", function()

        -- ...

        hl.bind("SHIFT + escape", hl.dsp.submap("reset"))
        hl.bind("escape", hl.dsp.submap("main_submap"))

        -- nested_two
        hl.bind("2", hl.dsp.submap("nested_two"))
        hl.define_submap("nested_two", function()

                -- ...

            hl.bind("SHIFT + escape", hl.dsp.submap("reset"))
            hl.bind("escape", hl.dsp.submap("main_submap"))
        
        -- /nested_two
        end)
    -- /nested_one
    end)
    
    hl.bind("escape", hl.dsp.submap("reset"))
-- /main_submap
end)

Automatically close a submap on dispatch

Submaps can be automatically closed or sent to another submap by appending , followed by a submap or reset.

hl.bind("SUPER + a", hl.dsp.submap("submapA"))

-- Sets the submap to submapB after pressing a.
hl.define_submap("submapA", "submapB", function()
    hl.bind("a", hl.dsp.exec_cmd("someCoolThing.sh"))
end)

-- Reset submap to default after pressing a.
hl.dsp.submap("submapB", "reset", function()
    hl.bind("a", hl.dsp.exec_cmd("someOtherCoolThing.sh"))
end)

Catch-All

You can also define a keybind via the special catchall keyword, which activates no matter which key is pressed.
This can be used to prevent any keys from passing to your active application while in a submap or to exit it immediately when any unknown key is pressed:

hl.bind("catchall", hl.dsp.submap("reset"))

Switchable keyboard layouts

The easiest way to accomplish this is to set this using XKB settings, for example:

hl.config({
    input =  {
	kb_layout = "us,cz",
	kb_variant = "qwerty",
	kb_options = "grp:alt_shift_toggle"
    }
})

Variants are set per layout.

Warning

The first layout defined in the input section will be the one used for binds by default.

For example: us,ua -> config binds would be e.g. "SUPER + A", while on ua,us -> "SUPER + Cyrillic_ef

You can change this behavior globally or per-device by setting resolve_binds_by_sym = 1. In that case, binds will activate when the symbol typed matches the symbol specified in the bind.

For example: if your layouts are us,fr and have a bind for "SUPER + A" you’d need to press the first letter on the second row while the us layout is active and the first letter on the first row while the fr layout is active.

You can also bind a key to execute hyprctl switchxkblayout for more keybind freedom. See Using hyprctl.

To find the valid layouts and kb_options, you can check out the /usr/share/X11/xkb/rules/base.lst. For example:

To get the layout name of a language:

grep -i 'persian' /usr/share/X11/xkb/rules/base.lst

To get the list of keyboard shortcuts you can put in the kb_options to toggle keyboard layouts:

grep 'grp:.*toggle' /usr/share/X11/xkb/rules/base.lst

Misc

Workspace bindings on non-QWERTY layouts

Keys used for keybinds need to be accessible without any modifiers in your layout.
For instance, the French AZERTY layout uses SHIFT + unmodified key to write 0-9 numbers. As such, the workspace keybinds for this layout need to use the names of the unmodified keys , and will not work when using the 0-9 numbers.

Note

To get the correct name for an unmodified_key, refer to the section on uncommon syms

-- On a French layout, instead of:
-- hl.bind(mainMod .. " + 1", hl.workspace(1))

-- Use
hl.bind(mainMod .. " + ampersand", hl.workspace(1))

For help configuring the French AZERTY layout, see this article.

Unbind

You can also unbind a key with the hl.unbind method, e.g.:

hl.unbind("SUPER + O")

This may be useful for dynamic keybindings with hyprctl, e.g.:

hyprctl eval 'hl.unbind("SUPER + O")'

Note

In unbind, key is case-sensitive It must exactly match the case of the bind you are unbinding.

hl.bind("SUPER + TAB", hl.focus.workspace("e+1"))
hl.unbind("SUPER + Tab") -- this will NOT unbind
hl.unbind("SUPER + TAB") -- this will unbind

Example Binds

Media

These binds set the expected behavior for regular keyboard media volume keys, including when the screen is locked:

hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"), { repeating = true })
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), { repeating = true })
hl.bind("XF86AudioMute",        hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { locked = true })

-- Requires playerctl
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"),   { locked = true })
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"),       { locked = true })

Disabling keybinds with one master keybind

If you want to disable all keybinds with another keybind (make a keybind toggle of sorts) you can just use a submap with only a keybind to exit it.

hl.bind(KEYS, hl.dsp.submap("clean"))
hl.define_submap("clean", function()
    hl.bind(KEYS, hl.dsp.submap("reset"))
end)

Remapping Caps Lock

You can customize the behavior of the Caps Lock key using kb_options.

To view all available options related to Caps Lock, run:

grep 'caps' /usr/share/X11/xkb/rules/base.lst

For example, to remap Caps lock to Ctrl:

hl.config({
    input = {
        kb_options = "ctrl:nocaps"
    }
})

To swap Caps Lock and Escape:

hl.config({
    input =  {
        kb_options = "caps:swapescape"
    }
})

You can also find additional kb_options unrelated to Caps Lock in /usr/share/X11/xkb/rules/base.lst.

Last updated on