Pyprland moves scratchpads and menus out of your Hyprland config
Scratchpads & many goodies for Hyprland & others [maintainer=@fdev31]
At a glance
- What is it?
- Pyprland is a Python plugin system that runs beside the Hyprland compositor and takes over the fiddly parts: scratchpads, nested menus, monitor settings and wallpapers. It is configurable in TOML, ships three console entry points, and has kept releasing through the 3.4 line.
- Who is it for?
- Pyprland fits a Hyprland user who wants scratchpads and dynamic menus without hand-writing stateful logic in the compositor config, and it is a poor fit for anyone who needs a single static keybinding or a plain DE. Before adopting it, confirm your Python is 3.11 or newer, compile the C client if you want the fast keybinding path, and check the hosted plugin pages, because the README alone does not describe the configuration format.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The scratchpad problem Pyprland takes off the config file
Hyprland keeps its behaviour in a configuration file, and keybindings are easy there. Anything that has to remember state is not. A scratchpad needs to hold on to the windows it opened, a client menu has to be assembled from the list of live windows, and a wallpaper that rotates on a schedule needs a timer that the compositor config has no concept of.
Pyprland moves that class of work into a separate Python program that talks to the compositor through its IPC interface. The README describes it as a plugin system that extends your graphical environment with scratchpads, dynamic popup nested menus, custom notifications and monitor settings, and it compares the idea to a GNOME tweak tool for Hyprland that can also run on other desktops.
The practical effect is that the compositor config keeps only the bindings, while a TOML file holds the logic and the plugin list. The project is aimed at people who already run Hyprland and have hit the ceiling of what a static config can express.
A plugin architecture that stopped requiring Hyprland
The repository is a Python package named pyprland with a plugin directory, a separate client directory holding the C source for the fast client, an examples directory and a tests directory. The topics attached to the project list asyncio, hyprland-ipc, scriptable and multiscreen, which matches the shape of the code: the compositor is reached over IPC and the plugins do their work asynchronously.
Version 3.0.0 is the pivot point in that architecture. The release notes for that major version list dynamic shell completions, better error handling and configuration validation, and the removal of the hard dependency on Hyprland. That last point is what lets the README claim the options can run on any desktop rather than only under Hyprland.
The 2.x history shows the same idea arriving in pieces. Hysteresis support for scratchpads landed in 2.0, the monitors and wallpapers plugins arrived in 2.2, and shell completion is a 3.0 feature. Configuration validation is worth noting, because a plugin system with a growing TOML surface is exactly where a typo in a key name turns into a silent no-op.
Three console scripts and a TOML file to get started
The packaging metadata names Python 3.11 or newer as the floor and lists aiofiles, aiohttp and pillow as the declared dependencies. The README's dependency section is more relaxed: aiofiles is described as optional but recommended, and pillow as optional but required for rounded borders in wallpapers. That gap matters for anyone who wants a minimal install, because the package metadata will pull all three in regardless.
The project exposes three entry points, and picking the wrong one is the most common first mistake. The main command is the long-running daemon that hosts the plugins.
[project.scripts]
pypr = "pyprland.command:main"
pypr-quickstart = "pyprland.quickstart:main"
pypr-gui = "pyprland.gui:main"`pypr` is the program you start for the session. `pypr-quickstart` pulls in an optional dependency group built around questionary, so it is the guided setup path rather than the runtime path. `pypr-gui` is a separate entry point in its own right. The README does not spell out the flags any of them take, so read the hosted documentation before wiring them into a session startup. The README also points to a repology packaging badge, which is where the project indicates that distribution packages exist for many systems rather than describing an install command itself.
pypr-client is meant for a keybinding, not for startup
The 2.4 release added a second binary, `pypr-client`, and the release notes are unusually specific about its intended use: it is meant to be used in the keyboard bindings, and explicitly not to start pypr on startup. Calling it from a keybinding keeps the compositor responsive because the client does a single small request instead of starting a Python interpreter for every action.
The README gives the shape of the binding, where the client is wrapped by uwsm-app so that it participates in the session manager's lifecycle.
$pypr = uwsm-app -- pypr-client
bind = $mainMod SHIFT, Z, exec, $pypr zoom ++0.5That example scales a scratchpad by half a step, which shows the general pattern: the client is addressed by name from a bind line. The binary is not shipped prebuilt, so it has to be produced from the C source in the client directory. The build recipe in the justfile compiles it with optimisation into the client directory as a binary of roughly seventeen kilobytes.
gcc -O2 -o client/pypr-client client/pypr-client.cSo the fast path has a build step attached to it. If you skip it, the README's own bindings cannot be copied as they are.
Scratchpads flipped to pinned behaviour in 2.4
The most likely source of surprise for an existing user is a single configuration key. Version 2.4 changed scratchpads to be pinned by default, and the old behaviour is restored with `pinned = false`. Anyone upgrading from an earlier release who finds that a scratchpad now covers the screen where it used to float has to add that key rather than hunt for a broken setting.
The scratchpad plugin itself grew across several releases. Hysteresis support arrived in 2.0, which lets a scratchpad distinguish a click from a drag when deciding whether to show itself. Version 2.3 brought better multi-window support and a rewritten `preserve_aspect` implementation described as i3 compatibility. Support for multiple client windows was added in 2.2, with 2.2.19 recommended for it, and the older `class_match` key was deprecated in favour of a separate scratchpad configuration page.
Monitor handling follows a similar path. The monitors plugin arrived in 2.3 for general monitor settings, and rotation support landed in 2.2.13. These are the details that decide whether an existing layout survives an upgrade, and none of them are visible from the project description alone.
Menus, wallpapers and input method switching
The plugin list covers more than scratchpads, and the way it accumulated shows the project's direction. A shortcuts menu appeared in 1.9, a client menu that fetches live window information in 1.10, wallpapers and a system notifier in 2.2, the gbar plugin in 2.2.6, a layout centering plugin in 1.8, and an fcitx5 switcher in the 2.5 series. The wallpapers plugin was refactored in 2.5 to support rounded corners and pausing.
That set of additions is the clearest argument for the plugin model. Each one is a small, self-contained piece of behaviour that can be enabled without touching the rest of the configuration, and the wallpapers plugin shows the trade-off: rounded corners depend on pillow, so an optional-looking feature quietly pulls in an image library.
The alternative approach is to hand-write the same behaviour into the compositor config, or into a script of your own that speaks to the compositor. The difference in approach is where the logic lives. A shell or Python script of your own does one job and you own its lifetime. Pyprland hosts many such jobs in one process, adds shell completion and configuration validation on top, and charges you for it with a Python runtime and a plugin list to keep in sync with the compositor version.
Where the project's own documentation runs out
The README is a pointer as much as a manual. Feature detail sits on a hosted documentation site, with a separate page per plugin, and the repository keeps a wiki generator in its justfile that regenerates those pages from the plugin source. The consequence is that the file you are most likely to read first will not tell you the TOML keys, and the pages change independently of the README.
The version requirements are also spread out. The release notes state that 1.8 requires Hyprland 0.30 or newer, 2.1 requires 0.37, 2.3 supports versions above 0.40.0, and 2.4.4 or newer is required for Hyprland 0.48.0. There is no single compatibility table, so pairing a Pyprland version with a compositor version means reading the history backwards.
The licensing side is simple and worth stating plainly: the project is published under the MIT licence, which is a permissive grant rather than a copyleft one, and it ships without a homepage field of its own beyond the repository URL. Nothing in the project imposes a share-alike obligation on configuration files or on other parts of a system.
A release line that is still moving
The last push to the main branch was on 2026-10-18, and that same day produced release 3.4.6. Before it came 3.4.5 on 2026-10-03 and 3.4.4 on 2026-09-03, so the 3.4 series is being revised several times a month rather than left to gather fixes for a year. The pyproject version field tracks the same number, which means a pinned install reproduces the release it names.
That pace is a real cost as well as a benefit. A project that ships patch releases every few weeks will keep asking you to check whether a plugin configuration key still matches, and the plugin pages are generated from source rather than frozen with a release. Development tooling in the repository is thorough for a plugin host of this size: the justfile carries recipes for a fast test run, a debugging test run with pdb, type checking, linting across several tools, dead code detection with a whitelist file, coverage reporting, plugin doc generation and release creation. The tests live in a tests directory with an asyncio marker available through pytest-asyncio.
Editorial conclusion
Pyprland fits a Hyprland user who wants scratchpads and dynamic menus without hand-writing stateful logic in the compositor config, and it is a poor fit for anyone who needs a single static keybinding or a plain DE. Before adopting it, confirm your Python is 3.11 or newer, compile the C client if you want the fast keybinding path, and check the hosted plugin pages, because the README alone does not describe the configuration format.
Frequently asked questions
What does Pyprland actually add to Hyprland?
It runs as a separate Python program and hosts plugins for scratchpads, nested popup menus, monitor settings, wallpapers and system notifications, so that logic lives in a TOML file instead of the compositor config. Since version 3.0.0 the hard dependency on Hyprland was removed, and the README says the options can run on any desktop.
How do I install Pyprland on Hyprland?
The packaging metadata requires Python 3.11 or newer and declares aiofiles, aiohttp and pillow as dependencies, and it exposes the commands pypr, pypr-quickstart and pypr-gui. The README points to a repology packaging badge and to the hosted documentation rather than giving a single install command, so the distribution route depends on your system.
How do I use pypr-client in a Hyprland keybinding?
The release notes say pypr-client is meant to be used in keyboard bindings and not to start pypr on startup, and the README shows it wrapped with uwsm-app inside a bind line. The binary has to be compiled first from the C source in the client directory using the justfile build recipe.
My scratchpad changed behaviour after upgrading Pyprland, what happened?
Version 2.4 made scratchpads pinned by default. The previous behaviour is restored by setting `pinned = false` in the scratchpad configuration.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/hyprland-community-pyprland)Was this useful?