# nwg-displays: a GUI that writes your sway, Hyprland and Niri output config

> nwg-displays is a GTK application for the sway, Hyprland and Niri Wayland compositors that lets you arrange monitors in a graphical interface and then writes the result out as the plain text or KDL files each compositor expects. It is MIT licensed, ships a Nix flake, and the README is candid about the fact that workspace to output assignment does not work under Niri's dynamic workspaces.

**nwg-piotr/nwg-displays** — Output management utility for sway, Hyprland and Niri

- Repository: https://github.com/nwg-piotr/nwg-displays
- Stars: 1,120 · Forks: 75
- Language: Python
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/nwg-piotr-nwg-displays

## A GUI for the part of a Wayland config nobody enjoys writing

nwg-displays is an output management utility for sway, Hyprland and Niri, described as inspired by wdisplays and wlay, and it is part of the nwg-shell project.

The problem is the output configuration block. In a Wayland compositor, a monitor setup is a set of declarations naming each output, its resolution, its position, its refresh rate and its scale, plus the workspace assignments that decide which screen a workspace lives on. Getting that right by hand means reading the compositor's documentation, matching connector names, and editing a file that the compositor reloads while you type.

The program is expected to do four things, and the README lists them as expectations rather than as shipped features, which is a fair and unusual thing for a README to admit. It should provide an intuitive GUI to manage multiple displays, apply settings, save outputs configuration to a text file, and save workspace to output assignments to a text file.

The fourth of those is scoped tightly. The support statement is that it supports sway, Hyprland and Niri only, so this is not a general display configuration tool for X11 or for GNOME, and a user on anything else gets nothing.

The intended user is a Wayland power user with two or three monitors whose layout changes, and who would rather click a diagram of the screens than edit connector names. The application is a GTK one, which the dependency list confirms with `gtk3` and `gtk-layer-shell`, the latter being what lets a window sit flush against the panel of a tiling compositor.

The licence is MIT, the primary language is Python, and the recent releases are v0.4.4 from 2026-08-24, v0.4.3 from 2026-05-17 and v0.4.2 from 2026-05-12. The last push to `master` was on 2026-09-30 and the repository is not archived.

## Three compositors, three configuration formats

The core of the tool is that it does not invent a configuration format. It writes the format each compositor already reads, which is why the installation instructions for the three are different.

For sway, the program saves the outputs configuration to a file that you include in your sway config, and it also saves a `~/.config/sway/workspaces` file defining the workspace to output associations, with lines of the form `workspace 1 output DP-1`.

For Hyprland, the generated file is `~/.config/hypr/monitors.conf` and you pull it in with a source line, and the workspace assignments go into `~/.config/hypr/workspaces.conf` with the same mechanism. For Hyprland 0.55 and greater, which uses Lua, the instruction changes to a `require` call instead of a `source` line.

For Niri, the program adds an include directive to `~/.config/niri/config.kdl` itself and saves the configuration to `~/.config/niri/monitor.kdl` in KDL format, and Niri will automatically reload the configuration when you apply settings in nwg-displays.

The Niri case is the one that behaves differently in kind. For sway and Hyprland the program writes a file and leaves the include to you, while for Niri it edits your main config file to add the include. That is a difference in invasiveness, and it is worth knowing which behaviour you are getting before you point it at a config you care about.

The Niri documentation also carries the limitation that matters most: Niri uses dynamic workspaces, so the workspace assignment feature is not available for Niri. The other half of the tool, the monitor layout, works; the workspace to output mapping does not.

## sway: two include lines and the workspace file

The sway integration is the simplest of the three, and it is a good illustration of the overall model.

The configuration saved to a file may be easily used in the sway config:

```text
...
include ~/.config/sway/outputs
...
```

The program also saves the `~/.config/sway/workspaces` file, which defines the workspace to output associations, and that file is included the same way:

```text
...
include ~/.config/sway/workspaces
...
```

The workspace file itself is a list of assignments, one per line:

```text
workspace 1 output DP-1
workspace 2 output DP-1
workspace 3 output DP-1
workspace 4 output eDP-1
workspace 5 output eDP-1
workspace 6 output eDP-1
workspace 7 output HDMI-A-1
workspace 8 output HDMI-A-1
```

The `DP-` and `eDP-` and `HDMI-A-` prefixes are the connector names as the compositor reports them, and that is the fragile part of the whole system. The README addresses it directly with the `--generic_names` flag, for the case where output names happen to be different on every restart, for example when you use Thunderbolt outputs.

The second flag is `--num_ws`, for the case where you use workspaces in a number other than 8. The generated file above has eight lines, and the program needs to be told when your setup differs.

`--num_ws` appears in the command line help, alongside the option that controls where the monitors file is written:

```text
$  nwg-displays -h
usage: nwg-displays [-h] [-m MONITORS_PATH] [-n NUM_WS] [-v]

options:
  -h, --help            show this help message and exit
  -m MONITORS_PATH, --monitors_path MONITORS_PATH
                        path to save the monitors.conf file to, default: ~/.config/hypr/monitors.conf
  -n NUM_WS, --num_ws NUM_WS
                        number of Workspaces in use, default: 10
  -v, --version         display version information
```

The default path is a Hyprland path, `~/.config/hypr/monitors.conf`, which tells you how the tool is organised internally even when you are configuring sway.

## Hyprland: source, require, and the autoreload trap

The Hyprland instructions are where the two configuration eras of that compositor meet, and the difference is a single word.

For the monitor configuration, instead of configuring as described in the wiki, you insert a source line:

```text
source = ~/.config/hypr/monitors.conf
```

And for version 0.55 and greater, which uses Lua, the instruction becomes:

```lua
require("monitors")
```

The same pair of changes applies to the default workspace and the binding of workspaces to a monitor, where the source line is `source = ~/.config/hypr/workspaces.conf` and the 0.55 form is `require("workspaces")`.

That much is straightforward. The line that will actually cost you an afternoon is the warning at the end of the section: do not set `disable_autoreload true` in Hyprland settings, or you will have to reload Hyprland manually after applying changes.

The reasoning is that the whole point of the tool is that you see the change happen. The compositor's live reload is what makes the graphical arrangement feel connected to the screen it configures, and turning it off removes that feedback loop, leaving a tool where you click a layout and then go and reload your compositor by hand. The warning exists because the two features are individually reasonable and jointly frustrating.

The same live reload is why the tool exists in the shape it does. Without it you would configure the outputs, save, and reload, and you would iterate on positions by editing text. With it you drag a screen around in a diagram and the compositor shows you the result immediately.

## Niri: KDL, an auto-added include, and one missing feature

Niri is the newest of the three targets and the most different in two ways.

The format is KDL, Niri's own configuration language, and the file the program writes is `~/.config/niri/monitor.kdl`. The program automatically adds an include directive to your `~/.config/niri/config.kdl` file:

```kdl
include "monitor.kdl"
```

So for Niri the tool is allowed to edit your main configuration, which it is not for the other two. That is a deliberate difference, and it is worth being comfortable with it, because an automatic edit of a config file is the kind of thing that should make you read the source before you run it.

The second difference is the feature gap. The note is explicit: Niri uses dynamic workspaces, so the workspace assignment feature is not available for Niri. Dynamic workspaces means a workspace is created when you need it rather than being a fixed numbered list, which means the idea of binding workspace number three to a particular screen does not apply the way it does in sway or Hyprland.

What remains is the part most people actually need, which is the monitor layout itself: resolution, position, scale and refresh rate per output, written in KDL and reloaded by Niri when you apply the settings.

The broader point is that this tool is a front end for three different configuration languages with three different conventions, and the differences are not cosmetic. Sway uses include lines in its own syntax, Hyprland uses source or require depending on its version, and Niri uses a KDL include that the program inserts for you. A single abstraction over three file formats is exactly the kind of thing that works well until one target gains a feature the other two do not have, which is what happened with workspace assignment.

## Installing it, and the small settings file it keeps

Installation is deliberately unremarkable. Install from your Linux distribution repository if possible, and the README carries a repology packaging status badge. Otherwise, clone this repository and run the `install.sh` script.

The dependency list is a GTK stack: `gtk-layer-shell`, `gtk3`, `python`, `python-gobject`, `python-i3ipc`, and the build tooling `python-build`, `python-installer`, `python-setuptools` and `python-wheel`, all of which are make dependencies. The presence of `python-i3ipc` is worth noticing, since it is the IPC library for talking to an i3-compatible compositor, which is how the tool reaches sway and the others.

The `setup.py` is short and says something interesting in a single line. The project is named `nwg-displays` at version 0.4.4, the licence is MIT, it requires Python 3.6.0 or newer, and `install_requires` is an empty list. The runtime dependencies are therefore not declared in the package metadata at all, which is why the README lists them separately.

The entry points explain the shape of the application. There is a `gui_scripts` entry, `nwg-displays = nwg_displays.main:main`, which is the graphical interface, and two `console_scripts` entries, `nwg-displays-apply` and `nwg-displays-toggle-wallpapers`, which are the command line paths for applying a saved profile and for toggling wallpapers.

The runtime configuration file is placed in your config directory, like `~/.config/nwg-displays/config`, and it is a simple JSON file:

```json
{
  "view-scale": 0.15,
  "snap-threshold": 10,
  "indicator-timeout": 500
}
```

The three keys are documented with the kind of honesty that comes from having written the code. `view-scale` does not need to be changed manually, because the GUI takes care of that. `snap-threshold` specifies the flush margin of widgets representing displays, and the author adds that he put the value in just in case, as he has no high-DPI display to test on. `indicator-timeout` determines how long, in milliseconds, the overlay identifying screens should be visible, and setting it to 0 turns the overlays off.

The repository also carries `uninstall.sh`, a `nwg-displays.desktop` entry and an icon, and Nix support through `flake.nix` and `flake.lock`.

## Where nwg-displays is the wrong tool

There are three cases where this application does not help, and the first is the most common.

The first is a single monitor. If you have one screen, there is nothing to arrange, and editing one output block in your compositor config takes less time than opening the tool.

The second is a compositor outside the supported three. The support statement is explicit, and for anything else there is no fallback, because the tool writes the compositor's own configuration format rather than a portable description of a layout. X11 desktops, GNOME on Wayland, KDE Plasma and anything else are out of scope by design.

The third is a workflow where the configuration lives elsewhere. If your sway or Hyprland config is generated by a dotfiles manager that owns the whole file, a tool that writes fragments into `~/.config` is a second source of truth that your dotfiles repository does not know about, and the two will disagree the first time someone runs the tool on a machine that is not the one they committed from. The nwg-shell project, of which this is a part, is the case where that is not a problem.

There is also a documentation point worth raising, and it is the same one the author raises about the high-DPI display. The README's own feature list is phrased as expectations, provide an intuitive GUI to manage multiple displays, apply settings, save outputs configuration to a text file, and save workspace to output assignments to a text file. That is an unusual way to describe shipped behaviour, and it tells you the feature set is still moving. The recent version numbers, 0.4.2, 0.4.3 and 0.4.4 within four months, support that reading.

The alternative approach, for comparison, is a small script that writes the output block from a known-good template. The difference in approach is that a script is reproducible and diffable. What nwg-displays adds is a visual arrangement of screens, which is genuinely useful when you are moving a laptop lid or plugging in a projector and want to see the result before you commit it to a config file.

## Conclusion

Adopt nwg-displays if you run sway, Hyprland or Niri with more than one monitor and you have ever edited an output block by hand, since the program writes exactly the include or source lines those compositors expect and keeps them in step when you change a resolution or a scale. Do not adopt it if you have a single display, if your compositor is not one of the three supported, or if you rely on workspace to output assignment under Niri, which the README rules out. Verify three things first: that the generated file is actually included by your compositor config, since the program writes a file and does not wire it in for sway, that Hyprland's `disable_autoreload` is not set, or changes will need a manual reload, and that the output names in your configuration match what the program detects, particularly for Thunderbolt displays where the names change between restarts.

## FAQ

### Which Wayland compositors does nwg-displays support?

sway, Hyprland and Niri only, as the README states. It writes the configuration format each of those reads, so it is not a general purpose display configuration tool.

### How do I install nwg-displays?

Install it from your Linux distribution repository if possible, and the README carries a repology packaging status badge. Otherwise, clone the repository and run the install.sh script. The dependencies are gtk-layer-shell, gtk3, python, python-gobject, python-i3ipc and the make based build packages.

### How do I use the files nwg-displays writes with sway?

Include the generated outputs file with `include ~/.config/sway/outputs` and the workspace assignments with `include ~/.config/sway/workspaces` in your sway config. The workspace file contains lines such as `workspace 1 output DP-1` mapping each workspace to an output.

### Why does nwg-displays not work for workspace assignment on Niri?

Because Niri uses dynamic workspaces, so the workspace assignment feature is not available for Niri. The monitor layout part of the tool works, and the configuration is written to ~/.config/niri/monitor.kdl with an include directive added to config.kdl.

### What is the indicator-timeout setting in nwg-displays?

It is part of the JSON runtime configuration placed in the config directory, like ~/.config/nwg-displays/config, alongside view-scale and snap-threshold. It determines how long, in milliseconds, the overlay identifying screens stays visible, and setting it to 0 turns the overlays off.

## Sources

- [Issues](https://github.com/nwg-piotr/nwg-displays/issues)
- [License: MIT](https://github.com/nwg-piotr/nwg-displays/blob/master/LICENSE)
- [nwg-piotr/nwg-displays on GitHub](https://github.com/nwg-piotr/nwg-displays)
- [README](https://github.com/nwg-piotr/nwg-displays/blob/master/README.md)
- [Releases](https://github.com/nwg-piotr/nwg-displays/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/nwg-piotr-nwg-displays
