I will archive niriswitcher once YaLTeR/niri#1704 (merged) has been released.
An application switcher for niri, with support for workspaces and automatic light and dark mode.

- Fast, beautiful application switching for niri
- Workspace-aware: switch apps in current or all workspaces
- Automatic light/dark mode with easy theming via CSS
- Customizable keybindings and appearance (icon size, window width, animation)
- MRU sorting and smooth animations
Screencast.from.2025-05-15.00-14-58.mp4
To install the development version use:
pipx install --system-site-packages git+https://github.com/isaksamsten/niriswitcher.gitappend @tag, where tag is a tagged version number, e.g., 0.1.2, to the url
to install a release version.
Note
You must use --system-site-packages to avoid having to build pygobjects from source.
You also need to install the following system packages:
python3-gobjectgtk4-layer-shelllibadwaita
These are the names of the packages on Fedora, but I'm sure they are distributed for other distributions.
For users of Fedora, I maintain a COPR built for every release.
dnf copr enable isaksamsten/niriswitcher
dnf install niriswitcherniriswitcher is now available in the AUR. You can use any of your AUR helpers to install.
yay -S niriswitcherA Nix package for niriswitcher is available. Add the following to your configuration:
environment.systemPackages = with pkgs; [
niriswitcher
];A home-manager module is also available.
First we need to execute the niriswitcher application. The program is
deamonized and waits for USR1 signal to be shown. In the niri
config.kdl-file we first start niriswitcher at startup:
spawn-at-startup "niriswitcher"Note
Replace niriswitcher with ~/.local/bin/niriswitcher if you installed using pipx and the binary is not on $PATH
Next, we add keybindings to send the USR1 signal to niriswitcher on Alt+Tab and Alt+Shift+Tab.
Note
Remember to synchronize the keybinding set in Niri, with the one set for niriswitcher. For example, if you use Mod+Tab to trigger niriswitcher ensure that modifier=Mod in config.toml.
You can either use niriswitcherctl, USR1 signal (deprecated) or direct DBus
calls to trigger niriswitcher.
Due to Python's slow startup time, this method introduces a slight delay of a few milliseconds (generally unnoticeable, but it increases the chance that niriswitcher may not be dismissed upon Alt release)
bind {
Alt+Tab repeat=false { spawn "niriswitcherctl" "show" "--window"; }
Alt+Shift+Tab repeat=false { spawn "niriswitcherctl" "show" "--window"; }
Alt+Grave repeat=false { spawn "niriswitcherctl" "show" "--workspace"; }
Alt+Shift+Grave repeat=false { spawn "niriswitcherctl" "show" "--workspace"; }
}This is generally a few milliseconds faster than niriswitcherctl.
Alt+Tab repeat=false { spawn "gdbus" "call" "--session" "--dest" "io.github.isaksamsten.Niriswitcher" "--object-path" "/io/github/isaksamsten/Niriswitcher" "--method" "io.github.isaksamsten.Niriswitcher.application" ; }
Alt+Shift+Tab repeat=false { spawn "gdbus" "call" "--session" "--dest" "io.github.isaksamsten.Niriswitcher" "--object-path" "/io/github/isaksamsten/Niriswitcher" "--method" "io.github.isaksamsten.Niriswitcher.application" ; }Change .application to .workspace if you want to bind "next application in
most recently used workspace"
Warning
Using USR1 to trigger niriswitcher has been deprecated in favour of DBus,
bind {
Alt+Tab repeat=false { spawn "pkill" "-USR1" "niriswitcher"; }
Alt+Shift+Tab repeat=false { spawn "pkill" "-USR1" "niriswitcher"; }
}By default, niriswitcher uses the following keybindings:
-
Alt+Tabselect next application -
Alt+Shift+Tabselect previous application -
Alt+Esccloseniriswitcherand do not focus -
Alt+qto close the selected application -
Alt+`to show the next workspace -
Alt+Shift+`to show the previous workspace -
Alt+[0 to 9]to show the workspace with index 1 to 10. Only enabled ifcurrent_output_onlyis set totrue -
Release
Altto focus to currently selected application and closeniriswitcher
The default mappings and modifier key can be configured in the config.toml file.
niriswitcher has a few options that we can change
- The icon size
- The maximum width of the switcher window (before scrolling)
- The scroll animation speed (set to 0 to disable)
- If single click change application without hiding the switcher (double clicking an application changes focus and hides the switcher)
- If
separate_workspacesis set totrue, the application switcher will show windows from the current workspace and enable workspace navigation. Iffalse, the switcher will show applications from all workspaces and disable workspace navigation. - Sort order of workspaces in the switcher.
- If
workspace.mru_sort_in_workspaceis set totruethe workspaces are sorted in order of last used when started with the most recently used window; otherwise in order of their workspace index (as in Niri moving up and down). - If
workspace.mru_sort_across_workspaceis set totrueworkspaces are sorted in order of last used when started with the most recently used window from the most recently used workspace; otherwise in order of workspace index.
- If
- Workspace name format in the switcher is controlled by
workspace_format. The format string supports{name},{output}and{idx}. - If
current_output_onlyistrueonly show windows and workspaces from the currently active output.
The configuration file is a simple .toml-file in
$XDG_CONFIG_HOME/niriswitcher/config.toml. This is the default configuration:
separate_workspaces = true
current_output_only = false
double_click_to_hide = false
center_on_focus = false
log_level = "WARN"
[appearance]
icon_size = 128
max_width = 800
min_width = 600
system_theme = "dark" # auto or light
workspace_format = "{output}-{idx}" # {output}, {idx}, {name}
[workspace]
mru_sort_in_workspace = false
mru_sort_across_workspace = true
[appearance.animation.reveal]
hide_duration = 200
show_duration = 200
easing = "ease-out-cubic"
[appearance.animation.resize]
duration = 200
easing = "ease-in-out-cubic"
[appearance.animation.workspace]
duration = 200
transition = "slide"
[appearance.animation.switch]
duration = 200
easing = "ease-out-cubic"
[keys]
modifier = "Alt_L"
[keys.switch]
next = "Tab"
prev = "Shift+Tab"
[keys.window]
close = "q"
abort = "Escape"
[keys.workspace]
next = "grave"
prev = "Shift+asciitilde"The modifier key keeps niriswitcher active (once released, niriswitcher
is closed and the selected application activated). It's implicitly part of the
other key mappings, so in the default configuration Alt+Tab moves to the next
application, Alt+Shift+Tab to the previous, and so forth. To change the
modifier key, select another key among: Alt, Super, Shift, or Control.
The other bindings are expressed as bindings, e.g.,
Shift+Tab or Control+j. Note that modifier is implicit in all
bindings.
Warning
When using Mod or Super as the modifier, the default niri config
maps Super+Escape to the toggle-keyboard-shortcuts-inhibit action.
Either change the default binding in your niri config or select
another binding for abort.
We can also change/improve the style of the switcher using a style.css file
in the same configuration directory.
The following CSS can be styled:
#niriswitcher: the main window#application-title: the title (above the icons)#workspace-name: the workspace name (above the icons)#workspaces: the workspace area.workspace: a workspace in the workspace area (contains the icons)#workspace-indicators: the workspace indicator area.workspace-indicator: the workspace indicator.workspace-indicator.selected: the currently selected workspace indicator.application-icon: the icon.application-name: the application name (below the icon).application-strip: the area behind the icons (inside the scroll).application: the application area (name + icon).application.selected: the currently selected application (by keyboard or mouse)
Example (see ./src/niriswitcher/resources/style.css for the default config):
To make the application title and selected application name red.
.application-title {
color: red;
}
.application.selected .application-name {
color: red;
}To make the application name visible for non-selected applications (but dimmed):
.application-name {
opacity: 1;
color: rgba(255, 255, 255, 0.6);
}
.application.selected .application-name {
color: rgba(255, 255, 255, 1);
}To hide the window title and workspace name:
#top-bar {
opacity: 0;
}To hide the workspace name:
#workspace-name {
opacity: 0;
}niriswitcher also uses style-dark.css to style the application in dark mode.
If you only want to change the colors, you only need to override the colors
(these are the default colors in light mode, see style-dark.css in the
resources folder for default colors in dark mode):
:root {
--bg-color: rgba(229, 229, 234, 1);
--label-color: rgb(58, 58, 60);
--alternate-label-color: rgb(44, 44, 46);
--dim-label-color: rgb(142, 142, 147);
--border-color: rgba(199, 199, 204, 0.95);
--highlight-color: rgba(209, 209, 214, 0.95);
--urgency-color: rgb(255, 69, 58);
--indicator-focus-color: rgba(10, 132, 255, 0.95);
--indicator-color: rgba(209, 209, 214, 0.95);
}Input on the default design is welcome
If Alt (the modifier key) is released before niriswitcher has been fully initialized, the window will not close unless the modifier key is pressed and released (to change to the next application) or Esc is pressed to close the window.

