Skip to content
dots
Esc
↑↓navigate↵open⌘Jpreview
On this page

niri

Scrollable-tiling Wayland compositor config — input, layout, window rules, per-display profiles, keybindings, and launcher scripts.

niri is a scrollable-tiling Wayland compositor. Here it runs with the Noctalia shell (bar, panels, OSD, notifications), vicinae as the launcher, and niri-dynamic-workspaces for named workspaces.

Files

Source (repo) Target
dot_config/niri/config.kdl ~/.config/niri/config.kdl
dot_config/niri/noctalia.kdl ~/.config/niri/noctalia.kdl
dot_config/niri/exact_config.d/*.kdl ~/.config/niri/config.d/*.kdl
dot_config/niri/scripts/executable_* ~/.config/niri/scripts/* (executable)
dot_local/bin/executable_niri-launch, executable_niri-launch-{ai,browser,terminal,webapp} ~/.local/bin/niri-launch*
dot_local/bin/executable_cp-appid ~/.local/bin/cp-appid

exact_config.d is a chezmoi exact directory. On apply, chezmoi deletes any file in ~/.config/niri/config.d/ that the repo does not manage, so stale fragments cannot linger. None of these files are templates.

  • ~/.config/niri/
    • config.kdl
    • noctalia.kdl
    • config.d/
      • 10-input-and-cursor.kdl
      • 20-layout-and-overview.kdl
      • 30-window-rules.kdl
      • 40-environment.kdl
      • 50-startup.kdl
      • 60-animations.kdl
      • 70-binds.kdl
      • 80-layer-rules.kdl
      • 90-outputs.kdl
      • 91-internal-display.kdl
      • 92-ultrawide.kdl
      • 93-apple-display.kdl
      • 94-portable-hdmi.kdl
      • 95-portable-usbc.kdl
    • scripts/
      • kill-phantom-display.fish
      • ndw-route-workspace.sh
      • niri-floating-sidebar.sh
      • niri-focus.sh
      • niri-move.sh

Installation

  • niri and xdg-user-dirs from pacman, noctalia-shell from the AUR. Declared under packages.arch.desktop in .chezmoidata/packages.yaml and installed by run_onchange_before_10-packages.sh.tmpl for desktop/laptop roles. See chezmoi scripts.
  • .chezmoiscripts/run_once_after_30-user-services.sh.tmpl runs once on Linux. If ~/.config/systemd/user/nirinit.service exists, it runs systemctl --user daemon-reload and systemctl --user enable nirinit.service. The script’s comment says other personal units (brain-lens, omp-loop, rclone, hermes, voxtype) ship as files only and are enabled by hand. The repo does not currently contain dot_config/systemd, so the unit must come from elsewhere for the enable step to run.
  • Tools the config calls but does not install: vicinae, cliphist/wl-paste, polkit-gnome, kbuildsycoca6, voxtype, niri-dynamic-workspaces, dot-screenshot, niri-smart-close, niri-smart-move, and the capitaine-cursors-light cursor theme. niri-smart-close, niri-smart-move, and dot-screenshot are called by path or name but are not in this repo.

Load order

config.kdl sets two top-level options and then includes the fragments in numeric order:

hotkey-overlay {
    skip-at-startup
}

screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"

include "config.d/10-input-and-cursor.kdl"
// … 20 through 90 …
include "config.d/90-outputs.kdl"

After the includes, config.kdl repeats inline input, layout, gestures, cursor, overview, window-rule, animations, and environment blocks, a leftover of the iNiR (illogical-impulse) base it started from. It then sets debug { honor-xdg-activation-with-invalid-serial }, which lets Noctalia v5 activate windows from notification actions. It includes ./noctalia.kdl and ends with prefer-no-csd. niri merges repeated sections and later values win, so the inline blocks override the fragments where they differ. Those differences are listed in each section below.

Input and cursor

config.d/10-input-and-cursor.kdl:

Setting Value
Keyboard layout us
Repeat delay / rate 400 ms / 50 Hz
Touchpad natural-scroll, tap, tap-button-map "left-right-middle", accel-speed 1.0
Tablet and touch map-to-output "eDP-1" (pen and touchscreen stay on the laptop panel)
Mod key Super; Alt when nested inside another compositor
Cursor capitaine-cursors-light, size 24, hide-when-typing

The inline input block in config.kdl repeats the same keyboard and touchpad values without accel-speed or the tablet/touch mappings, so those two settings from the fragment still apply.

Layout and overview

config.d/20-layout-and-overview.kdl:

Setting Value
center-focused-column never
background-color transparent (the Noctalia wallpaper shows through as backdrop)
default-column-width proportion 0.5
preset-column-widths 0.33333, 0.5, 0.66667
Focus ring width 1.5
Border off (width 1.5 if enabled)
Shadow off (softness 30, spread 5, offset y=5 if enabled)
Tab indicator place-within-column, hide-when-single-tab, position left
Overview zoom 0.4, workspace shadow off

The gestures { hot-corners { } } block is empty in the fragment. The inline block in config.kdl sets hot-corners { off }, so hot corners are disabled.

Colors (Noctalia)

noctalia.kdl is included last, so its colors override the fragment’s placeholder colors (#7fc8ff ring, #ffc87f border):

Element Active Inactive Urgent
Focus ring #a398b3 #1e1c21 #fd4663
Border #a398b3 #1e1c21 #fd4663
Tab indicator #a398b3 #4a4158 #fd4663
Recent-windows highlight #a398b3 — #fd4663

Shadow color is #1e1c2170 and the insert hint is #a398b380. The palette follows the Noctalia shell’s color scheme, not cyberdream.

Window rules

config.d/30-window-rules.kdl:

Match Rule
All windows geometry-corner-radius 10, clip-to-geometry true; background blur on, xray off
dev.noctalia.Noctalia (Noctalia v5 settings) Floating, 1080 × 920
Folo, Spotify, zen, brave-origin, gemini-desktop, claude-desktop Opacity 0.9, tiled, blur, no border background, open focused
kitty, zed, obsidian, Beeper, chromium, com.mitchellh.ghostty Opacity 0.9, tiled, blur, no border background, open focused
org.gnome.Nautilus, com.gabm.satty Floating, focused, width proportion 0.5
Title Picture-in-Picture (app zen) or Picture in picture Floating, not focused, opacity 1.0, 24 px from the top-right corner, on eDP-1
zed, obsidian, sticky-notest-canvas, title Sticky Notes, vacuumtube, Fladder, kitty, kitty-herdr, kitty-tmux Open on the Apple Studio Display

Global blur settings: passes 4, offset 5.0, noise 0.02, saturation 2.0. The inline window-rule in config.kdl sets geometry-corner-radius 8 after the fragment, so the effective radius is 8.

Environment

config.d/40-environment.kdl (repeated verbatim inline in config.kdl):

Variable Value Purpose
XDG_CURRENT_DESKTOP niri
XDG_MENU_PREFIX plasma- Dolphin/KDE file associations
QT_QPA_PLATFORM wayland
ELECTRON_OZONE_PLATFORM_HINT auto Native Wayland for Electron
QT_LOGGING_RULES quickshell.dbus.properties=false Silence quickshell D-Bus logs
QT_QPA_PLATFORMTHEME kde Qt colors from kdeglobals (needs plasma-integration)
QT_STYLE_OVERRIDE Darkly Qt widget style
INIR_VENV, ILLOGICAL_IMPULSE_VIRTUAL_ENV $HOME/.local/state/quickshell/.venv Quickshell Python venv

Startup

config.d/50-startup.kdl spawns at login:

Command Purpose
systemctl --user import-environment XDG_MENU_PREFIX && kbuildsycoca6 Export the menu prefix to systemd, rebuild the KDE service cache
/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 Polkit agent
noctalia Shell: bar, panels, notifications, OSD, wallpaper
niri-dynamic-workspaces daemon Named dynamic workspaces
vicinae server Launcher backend
wl-paste --watch cliphist store Clipboard history

Animations

config.d/60-animations.kdl makes every animation a critically damped spring (damping-ratio=1.0, epsilon=0.0001) with high stiffness: workspace-switch 4000, window-open 5000, window-close 6000, horizontal-view-movement 4500, window-movement 5000, window-resize 5500, config-notification-open-close 5000, screenshot-ui-open 5000.

The inline animations block in config.kdl comes later and overrides these with softer springs tuned to match quickshell’s Material motion:

Animation Damping ratio Stiffness
workspace-switch 0.78 1350
window-open 0.82 1125
window-close 0.88 2025
horizontal-view-movement 0.80 1238
window-movement 0.85 1463
window-resize 0.88 1575
config-notification-open-close 0.90 1800
screenshot-ui-open 0.85 1688

Keybindings

All bindings from config.d/70-binds.kdl. Mod is Super (Alt when niri runs nested). Super+… and Mod+… are the same modifier. The table groups bindings by the file’s comment headers.

System and launcher

Keys Action
Mod+Tab Toggle overview (no repeat)
Mod+Shift+E Quit niri
Mod+Escape Toggle keyboard-shortcuts inhibit (cannot itself be inhibited)
Mod+Space vicinae toggle — launcher
Mod+Shift+Space vicinae emoji search
Mod+Shift+Return vicinae window switcher
Super+Alt+Ctrl+Return vicinae window switcher
Mod+BackSpace Focus previous window
Mod+P vicinae clipboard history
Mod+Ctrl+R Toggle the Noctalia prdlk/ostt:recorder plugin
Mod+Period Noctalia control center panel
Mod+Comma Noctalia settings
Mod+Shift+Q Noctalia session menu (hotkey-overlay title “Session Menu: noctalia session”)
Mod+F24 voxtype record toggle --profile hermes — dictation

Screenshots

Keys Action
Mod+S dot-screenshot region
Mod+Shift+S dot-screenshot window
Mod+Ctrl+S dot-screenshot monitor-focused
Mod+Ctrl+Shift+S dot-screenshot monitor-all
Print niri screenshot UI
Ctrl+Print Screenshot the screen
Alt+Print Screenshot the window

Applications

Keys Action
Mod+Shift+G Goliath: kitty --class=kitty-goliath --title=goliath --config ~/.config/kitty/goliath.conf (mosh + tmux; see kitty)
Super+Ctrl+A Claude desktop (via vicinae)
Super+Ctrl+G Gemini desktop (via vicinae)
Super+Alt+Ctrl+H niri-launch-ai — cycle Claude/Hermes windows
Super+Alt+Ctrl+J niri-launch-terminal — cycle terminals
Super+Alt+Ctrl+K niri-launch-browser — cycle Brave Origin
Super+Alt+Ctrl+O niri-launch obsidian
Super+Alt+Ctrl+Y niri-launch-webapp — cycle web apps
Super+Alt+Ctrl+W tldraw offline (via vicinae)
Super+Alt+Ctrl+E Fastmail (via vicinae)
Super+Alt+Ctrl+B Beeper (via vicinae)
Super+Alt+Ctrl+M Beeper (via vicinae)
Super+Alt+Ctrl+G Ghostty (via vicinae)
Super+Alt+Ctrl+L Linear web app (via vicinae)
Super+Alt+Ctrl+T Todoist (via vicinae)
Super+Alt+Ctrl+N Open the Noctalia noctalia/notes panel
Super+Alt+Ctrl+F Nautilus (via vicinae)
Super+Alt+Ctrl+Space Sticky Notes Canvas (via vicinae)

Dynamic workspaces

Keys Action
Super+Shift+Alt+Ctrl+W niri-dynamic-workspaces switch w
Super+Shift+Alt+Ctrl+H niri-dynamic-workspaces switch h
Super+Alt+Ctrl+X niri-dynamic-workspaces switch x

Focus and movement

Keys Action
Mod+H Focus column left (wraps to last)
Mod+L Focus column right (wraps to first)
Mod+J Focus window below, or workspace below
Mod+K Focus window above, or workspace above
Mod+Shift+H niri-smart-move left
Mod+Shift+L niri-smart-move right
Mod+Shift+J Move column to workspace below
Mod+Shift+K Move column to workspace above
Super+Ctrl+H Focus first column
Super+Ctrl+L Focus last column
Super+Ctrl+Shift+H Move column to first
Super+Ctrl+Shift+L Move column to last
Mod+Q niri-smart-close (no repeat)

Monitors

Keys Action
Mod+Left Focus monitor left
Mod+Up Focus monitor up
Mod+Right Focus monitor right
Mod+Shift+Down Move column to monitor right
Mod+Shift+Up Move column to monitor left
Super+O Focus next monitor
Super+Ctrl+J Focus next monitor
Super+Ctrl+K Focus previous monitor
Super+Shift+O Move column to next monitor
Mod+Ctrl+Shift+J Move column to next monitor
Mod+Ctrl+Shift+K Move column to previous monitor

Layout and sizing

Keys Action
Mod+R Cycle preset column widths
Mod+Shift+R Cycle preset window heights
Mod+F Expand column to available width
Mod+Shift+F Maximize column
Mod+Ctrl+F Fullscreen window
Super+C Center column
Mod+Ctrl+C Center visible columns
Mod+Ctrl+O Toggle window floating
Mod+W Toggle tabbed column display
Alt+Shift+H Column width −10%
Alt+Shift+L Column width +10%
Alt+Shift+J Window height −10%
Alt+Shift+K Window height +10%

Hardware keys

All of these work while the screen is locked (allow-when-locked=true) and go through Noctalia IPC, so the Noctalia OSD shows feedback. The commented v4: lines in the file are the old qs -c noctalia-shell ipc call … forms.

Keys Action
XF86AudioRaiseVolume noctalia msg volume-up
XF86AudioLowerVolume noctalia msg volume-down
XF86AudioMute noctalia msg volume-mute
XF86AudioMicMute noctalia msg mic-mute
XF86AudioNext noctalia msg media next
XF86AudioPrev noctalia msg media previous
XF86AudioPlay / XF86AudioPause noctalia msg media toggle
XF86MonBrightnessUp noctalia msg brightness-up
XF86MonBrightnessDown noctalia msg brightness-down

Layer rules

config.d/80-layer-rules.kdl:

  • Noctalia surfaces matching ^noctalia-(bar-…|notification|dock|panel|attached-panel|osd)$ get background-effect { xray false }, so their blur samples what is actually behind them. Noctalia publishes blur regions itself when ext-background-effects is available.
  • ^noctalia-wallpaper is placed within the backdrop (place-within-backdrop true). The wallpaper stays still while workspaces move and shows in the overview.
  • layout { background-color "transparent" } and overview workspace-shadow { off } are restated here so the backdrop is always visible.

Outputs and display profiles

config.d/90-outputs.kdl turns DP-5 off and includes the per-display profiles. An older Apple Display block and a DP-4 off block remain as comments.

include "91-internal-display.kdl"
// include "92-ultrawide.kdl"
include "93-apple-display.kdl"
include "94-portable-hdmi.kdl"
// include "95-portable-usbc.kdl"

The Apple Studio Display also exposes a phantom connector (DP-4/5/6, varies per boot) whose only mode is 640×480. A commented spawn-at-startup for scripts/kill-phantom-display.fish handles it by mode signature, since connector name and EDID both shift.

Tianma Microelectronics Ltd. TL134ADXP03 Unknown, the laptop panel. Active.

Setting Value
Position / scale 0,0 / 1.5
VRR on-demand=true
Gaps 8
Default column width 1.0
Preset widths 0.5, 1.0
Centering always-center-single-column, center-focused-column "never"

Named workspaces 1, 2, 3 open on this panel. Three window rules open these apps maximized and focused on workspace 1, without border backgrounds (all three target "1" as written):

  • chrome-youtube.com__-Default, chromium
  • org.gnome.Calendar, discord, slack, com-fastmail-fastmail, gemini-desktop, Beeper, com.anthropic.Claude, Hermes, todoist
  • WebApp-CloudflarePersonal7594, WebApp-CloudflareWalletbase8881

Samsung Electric Company S34CG50 HNTY501103. Included in the repo but commented out.

Setting Value
Mode 3440x1440@100.00
Position / scale 0,0 / 1
VRR on-demand=true
Gaps 16
Default column width 0.3333
Preset widths 0.3333, 0.5, 0.6667
Centering always-center-single-column, center-focused-column "never"

Apple Computer Inc StudioDisplay 0x984262A7. Active.

Setting Value
Scale 1.75
Gaps 8
Default column width 0.5
Preset widths 0.25, 0.3333, 0.5, 0.6667
Centering always-center-single-column, center-focused-column "never"

PNP(AOP) 16PM1QB 14490001D3X00. Active.

Setting Value
Transform 90 (portrait)
Gaps 2
Default column width 1.0
Preset widths 0.5, 1.0
Centering always-center-single-column, center-focused-column "never"

ASUSTek COMPUTER INC ASUS MB16AC M8LMTF195749. Included in the repo but commented out.

Setting Value
Position 0,0
Gaps 2
Default column width 1.0
Preset widths 0.5, 1.0
Centering always-center-single-column, center-focused-column "never"

To switch a profile, uncomment or comment its include line in 90-outputs.kdl. niri reloads config on save.

Scripts

Deployed to ~/.config/niri/scripts/. 70-binds.kdl binds none of them, so they run by hand or from other tools.

Script Usage Behavior
kill-phantom-display.fish no args Polls niri msg -j outputs up to 10 times, 1 s apart. Any output whose only mode is 640×480 gets niri msg output <name> off. Does not handle mid-session hotplug.
ndw-route-workspace.sh $NDW_WORKSPACE_KEY or $1 niri-dynamic-workspaces on_create hook. Keys y g m x z p l a move the workspace to HDMI-A-1. Keys b f s w c d o move it to the Apple Studio Display (resolved by make/model through niri msg --json outputs), or to HDMI-A-1 if that display is absent. Other keys exit silently.
niri-floating-sidebar.sh toggle | hide | flip | reorder | help Per-workspace floating sidebar on the right edge. Windows are 550 px wide and stack into 4 slots (more as needed) with 15 px gaps and 30/50/20 px top/bottom/side margins. toggle floats the focused window into the sidebar or restores it to tiling at its original size. hide slides the sidebar off-screen, leaving a 70 px sliver. flip switches bottom-up and top-down stacking. State is kept in /tmp/niri.toggle.<workspace>.*.
niri-focus.sh up | down Cascading focus: next window in the column, then the next occupied workspace (skipping the trailing empty one), then the adjacent monitor. Wraps to the far-end monitor at the edge.
niri-move.sh up | down Cascading move: reorder within the column, then move to the next workspace (including the empty trailing one), then the adjacent monitor. Wraps at the edge.

Launchers

niri-launch

~/.local/bin/niri-launch <app> focuses an app’s window, cycles through its windows on repeated presses (sorted by window id), or launches it when none is open.

Argument Windows matched (app-id) Launched when none
webapp ^(webapp-|chrome-) (Firefox and Chromium/Brave site-specific apps) Re-runs as webapp-youtube
webapp-NAME WebApp-Name (case-insensitive) gtk-launch on ~/.local/share/applications/WebApp-Name*.desktop
kitty any app-id starting with kitty kitty
kitty-CLASS exactly that class kitty --class kitty-CLASS (optional command from the empty KITTY_CMDS map)
ghostty ghostty nothing, prints a message
browser ^(zen|brave|chromium|firefox) zen
terminal kitty|alacritty|ghostty kitty
ai claude|hermes hermes desktop
anything else exact app-id the argument as a command

Wrappers

Each wrapper resolves its own symlink and execs niri-launch from the same directory:

Script Runs Bound to
niri-launch-ai niri-launch ai Super+Alt+Ctrl+H
niri-launch-terminal niri-launch terminal Super+Alt+Ctrl+J
niri-launch-browser niri-launch brave-origin (exact app-id, not the browser mode) Super+Alt+Ctrl+K
niri-launch-webapp niri-launch webapp Super+Alt+Ctrl+Y

cp-appid

cp-appid lists every .desktop file (via fd) in /usr/share/applications, /usr/local/share/applications, ~/.local/share/applications, the system and user Flatpak export dirs, and /var/lib/snapd/desktop/applications. It shows each entry’s Name= and ID in fzf, then copies the chosen ID to the clipboard with wl-copy. Use it to find app-ids for window rules and niri-launch.

  • Hyprland: the Omarchy setup with the same key layout.
  • kitty: kitty-goliath, kitty-herdr, and kitty-tmux classes referenced by the rules.

Was this page helpful?