A look at the Quickshell desktop-component toolkit
Quickshell is a toolkit for building desktop components, such as toolbars or menus. It uses QML, which is a declarative language for designing GUI applications. Quickshell helps developers create graphical tools for common desktop use cases with a focus on ease of development. It offers a convenient method for writing user interfaces and has been adopted by a number of projects, such as caelestia-shell and DankMaterialShell, that provide desktop environments for minimal window managers like Sway and niri.
A high-level toolkit
Quickshell arose from a need for easier methods to build interfaces for desktop components, such as a system tray or management interface for audio devices. Other toolkits can achieve similar results but require a significant up-front investment to gain familiarity with their development environments and libraries. As we will see below, it can be used to easily create utilities, such as a desktop launcher or toolbar, that are useful for a desktop environment.
The project's creator and lead developer "outfoxxed" tried existing solutions like Elkowar's Wacky Widgets (eww), another desktop widget system, but ran into challenges that prompted them to look elsewhere.
Their preference for Qt over GTK also led outfoxxed toward a custom solution that
eventually became Quickshell. "Some time in December 2023 I was testing Qt
Quick for viability in another project that never happened, and I noticed that
it was really approachable since the language has features like automatic
reactive bindings
". That refers to property
binding, which allows QML to propagate changing values through parts of its
structure. For example, the text of a button does not need special treatment to
reflect updates if its string value incorporates another value that changes in
response to an event being triggered. The bindings QML establishes between
properties handles updates by tracking dependencies that change over time and
updating them as needed.
Qt and GTK are popular for writing desktop components on Linux. However, using them means writing lower-level code and involves compilation steps that incur delays during iterative development. Quickshell bridges the gap between desktop APIs and the Qt graphical toolkit. It also adds the ability to iterate rapidly when building desktop interfaces. With just Quickshell installed, users can create their own graphical interfaces with any of the types listed in its documentation, such as PopupWindow or SystemTray, available for use directly in QML documents, which are stored as .qml files. Although it's well-suited for minimalist desktops, it is also useful for creating desktop widgets and other utilities to be used with more full-fledged desktop environments like GNOME and KDE Plasma as well.
Additionally, while APIs for services like Pipewire are generally available on most Linux distributions, each API requires familiarity with those individual services to properly use them. Types like Pipewire provided by Quickshell make the process of working with these services much easier.
Quickshell had its first release, version 0.1, in June 2024. The most recent, version 0.3, was released in June 2026 with new API features for networking, polkit integration, as well as many other enhancements and bug fixes. It is packaged for many Linux distributions, though it is not available in Debian 13, but is in testing. Debian packages are also available on the Open Build Service. It is provided under the LGPLv3 and hosted on on GitHub, which is the primary channel for user contributions and issue tracking, with a mirror hosted on an independent Forgejo server.
Outfoxxed is the primary committer and reviewer, responsible for the bulk of development. However, community contributions like wired-networking support have helped to significantly improve the project. The project's contribution policy neither bans nor endorses the use of LLM-assisted coding but does require that changes are owned by human developers and forbids the use of completely automated tooling to contribute code.
A shell ecosystem
Minimalist compositors and window managers intentionally provide little more than the core capabilities needed to display and manage windows; users are left to their own devices to choose other components to build the desktop they want. Projects such as Waybar (which provides standalone toolbars) or Rofi (an application launcher) can close the gap. There are many other similar projects that provide specific features to window managers or compositors, but assembling a complete desktop environment from small applications takes some effort.
Popular Quickshell-based projects, such as caelestia-shell and DankMaterialShell, offer a middle ground. They do not provide feature parity with desktop environments, such as GNOME and Plasma, but they provide many of the features users might want from a desktop when using bare-bones compositors such as Sway and niri.
Desktop shells built with Quickshell are usually configurable with user-friendly graphical interfaces rather than text-based configuration files. Interfaces built with Quickshell can be styled via QML types that add effects like animations and styles. The DankMaterialShell theme gallery has many examples of how one might style Quickshell. DankMaterialShell also has support for plugins, which lets users add third-party widgets (or create their own).
Some of Quickshell's features depend upon the window manager or compositor in use. For example, using background transparency blurring in Quickshell while on Wayland requires a compositor that supports the ext-background-effect Wayland protocol. Support for modern Wayland compositors is usually consistent across projects that rely on foundational libraries like wlroots (Sway) and Smithay (such as niri and COSMIC). In the case of niri, for example, support for ext-background-effect was added on the 26.04 release.
A trove of types
QML is used to express the structure and function of interface elements that are managed by the Qt framework. Quickshell is configured via QML files that declare objects derived from the upstream QML types. These types are high-level abstractions that can be instantiated to create elements like buttons or windows.
The syntax of QML can be thought of as a layer of abstraction above using Qt directly. Elements are declared rather than constructed in an imperative style. For example, while Qt widgets like layouts and labels might be created like this in Python:
from PyQt6.QtWidgets import (QGridLayout, QLabel)
layout = QGridLayout()
label = QLabel("Using Qt 6 in Python.")
layout.addWidget(label)
The equivalent QML instead creates the desired structure declaratively:
import QtQuick.Controls
import QtQuick.Layouts
GridLayout {
Label {
text: "Using Qt 6 with QML."
}
}
To support interactive elements like buttons, events are bound to scripts written in JavaScript that are compiled internally by the QML engine.
Quickshell extends the available QtQuick types with its own types that provide additional capabilities. (QtQuick is the standard library of types available for use with QML.) Many of the bundled Quickshell types serve as abstractions over common APIs and other desktop interfaces.
For example, Quickshell provides the Process type that is generally useful for running shell commands and capturing their output. However, the UPower type grants more convenient access to host information: rather than parsing files in /sys/class/power_supply to discover a battery's charge level, GUI elements can rely on the UPowerDevice.percentage property for a device.
Quickshell types like Bluetooth, Networking, and Sockets let developers work with high-level properties to easily make use of system state. For example, text content or the icon path of an object can react when the connected property of a Network type changes.
There are some tradeoffs in using Quickshell versus building natively with Qt or GTK. While QML may be embedded in different languages like Python or C++, QML scripting is limited to JavaScript (although developers can extend QML via some techniques with C++). GTK or Qt may be better suited for building interfaces with specific languages, or with more complex backend logic, where a greater degree of control is available.
QML and declarative interfaces
To illustrate a concrete example of a GUI built with Quickshell, the following minimal QML imports types from the QtQuick library and creates a Text object with its text property set to a static string value:
import QtQuick
Text {
text: "Hello from Quickshell!"
}
Quickshell will use the QML file ~/.config/quickshell/shell.qml by default when running the quickshell command, or specific QML files may be passed to quickshell with the -p/--path flag. Changes to the text property of the Text object will be reflected immediately when saving the shell.qml file. The resulting interface shown here is running on a default niri desktop configuration.
That is probably not very useful, by itself, however. The next example shows how to change shell.qml to bind events to JavaScript expressions and make an interface interactive. It builds a FloatingWindow with a stacked row layout comprised of a Text object and a Button. Clicking the button increments the customData variable tracking the number of clicks and updates the text property in response to the changing value.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
FloatingWindow {
property var customData: 0
ColumnLayout {
Text {
text: `Clicks: ${customData}`
}
Button {
text: "Increment"
onClicked: customData += 1
}
}
}
This example uses several types from the QtQuick imports in addition to the FloatingWindow type from the Quickshell namespace. Writing to the file is sufficient to prompt Quickshell to rebuild the interface and create a functional interactive window. Clicking on the Button element labeled "Increment" will dynamically update the Text object in the Quickshell window.
As a final demonstration of Quickshell's capabilities, the following QML uses DesktopEntries to retrieve Freedesktop.org desktop application specification files and construct a scrollable grid of buttons to launch corresponding entries, closing the launcher after selection.
The FloatingWindow also uses several Quickshell helper functions like Quickshell.iconPath to resolve icon paths and Quickshell.execDetached to run the desktop entry's executable in the background. The .execDetached function expects a list of arguments, passed here as an array with the desktop entry's execString as the singular element.
The GridView type works by accepting a collection of items passed to its model property (in this case, an array called DesktopEntries.applications) and instantiating a new object for every element in that collection. The delegate property controls how to consume each element of the collection: in this example, a new RoundButton object is created with the modelData variable available to retrieve other values like modelData.icon for the icon path of the desktop entry. The addition of a ToolTip provides application names as hover text when the mouse cursor is placed over the icon. Calls to functions like Qt.quit use the always-imported Qt namespace.
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Widgets
FloatingWindow {
title: "LWN Launcher"
ScrollView {
anchors.fill: parent
GridView {
id: grid
cellWidth: 48
cellHeight: 48
model: DesktopEntries.applications
delegate: RoundButton {
radius: 0
implicitWidth: grid.cellWidth
implicitHeight: grid.cellWidth
hoverEnabled: true
ToolTip.text: modelData.name
ToolTip.visible: hovered
contentItem: IconImage {
source: Quickshell.iconPath(modelData.icon)
}
onClicked: {
Quickshell.execDetached([modelData.execString]);
Qt.quit()
}
}
}
}
}
The end result is a simple grid of icon buttons that displays all installed desktop applications with Freedesktop entries. Importantly, the Quickshell widget is portable: most desktop applications install Freedesktop.org entries, and there are no window manager or compositor-specific elements to the QML. The overall result should be similar whether started from niri, Sway, Plasma, or any other standard desktop environment. There are countless ways to further customize this GUI or enrich it with additional features.
Conclusion
My own experience with Quickshell has been in combination with niri on NixOS. Although I originally wrote my own toolbars, launcher, and other elements, DankMaterialShell has now become my default desktop shell. Quickshell remains useful to write focused utilities that require GUIs like menus for scripts or silly proof-of-concepts. However, DankMaterialShell offers a degree of polish that might be hard to otherwise achieve on my own. As with many young projects, the main drawback tends to be the rapid pace of changes that occasionally necessitate manual intervention when plugins break or versions drift, but anecdotally, stability seems to be improving rapidly.
Both Quickshell and its related desktop-shell projects boast particularly well-written and comprehensive documentation for further reading. In addition to the documentation already linked, Quickshell provides an introduction to writing QML. The DankMaterialShell documentation includes useful guides for authoring plugins as well as how to manage and update an existing installation.
Quickshell is relatively new on the Linux desktop scene, but its quick adoption seems to be making an impact across Linux distributions and desktop environments.
| Index entries for this article | |
|---|---|
| GuestArticles | Langlois, Tyler |
