-
-
Notifications
You must be signed in to change notification settings - Fork 1
Quickshell Beginners guide
Hi! I'm Tony. Do you ever look at your computer's desktop and think, "I wish I could change that"? Well, you can! This guide will teach you how to build your own custom bar for the top of your screen from scratch. You don't need to know anything about coding. I'll explain every single step like we're putting together a LEGO set.
By the end, you will have built a bar that shows:
- Which virtual desktop you're using (you can click to switch!)
- How hard your computer's brain (CPU) is working
- How full your computer's short-term memory (RAM) is
- A clock that updates live
Ready? Let's go!
First, we need to install the tool, Quickshell. Think of it as the app we use to build and run our custom bar.
-
What you need: A computer running Linux. Quickshell works best with a newer display system called Wayland. We'll be using Hyprland as our window manager (the thing that organizes your windows).
-
How to install: The command is different depending on your type of Linux. Open your Terminal app (like Command Prompt, but for Linux) and type the one that matches your computer:
-
For Arch Linux: Type
yay -S quickshell-gitand press Enter. -
For NixOS: You need to add
quickshellto a special list in your system's configuration file. The code you need isenvironment.systemPackages = with pkgs; [ quickshell ];. - For other systems: Check the Quickshell GitHub page for instructions for your specific version.
-
For Arch Linux: Type
Let's make sure everything works by creating the simplest program possible.
-
Create your project folder:
- Open your file manager and go to your Home folder.
- Create a new folder called
.config. - Inside
.config, create another folder calledtestshell. Your full path should be~/.config/testshell/.
-
Create your first code file:
- Inside the
testshellfolder, create a new file named01-hello.qml. - Open this file with any text editor (like Notepad, but for coding).
- Inside the
-
Copy and paste this code:
import Quickshell import QtQuick FloatingWindow { visible: true width: 200 height: 100 Text { anchors.centerIn: parent text: "Hello, Quickshell!" color: "#0db9d7" font.pixelSize: 18 } }
Let's understand what we just did (don't worry, it's simple!):
-
Line 1 & 2 (
import): This is like opening your toolbox. You're saying, "Get me the Quickshell tools and the basic UI tools." -
FloatingWindow { }: You're creating a new window.visible: truemeans "show it."widthandheightset its size in pixels. -
Text { }: You're putting text inside the window.-
anchors.centerIn: parentis like a magnet that snaps the text to the center. -
text:is what the words say. -
color:is the text color (this is a blueish color). -
font.pixelSize:is how big the letters are.
-
-
Line 1 & 2 (
-
Run it!
- Go back to your Terminal.
- Type this command and press Enter:
qs -p ~/.config/testshell/01-hello.qml - You should see a small blue window pop up with the text "Hello, Quickshell!" If you do, you're all set!
The floating window is cool, but we want a bar that sticks to the top of the screen, like the one you already have. Let's upgrade!
-
Create a new file in the same folder called
02-bar.qml. -
Copy and paste this new code:
import Quickshell import Quickshell.Wayland import QtQuick PanelWindow { anchors.top: true anchors.left: true anchors.right: true implicitHeight: 30 color: "#1a1b26" Text { anchors.centerIn: parent text: "My First Bar" color: "#a9b1d6" font.pixelSize: 14 } }
What changed?
- We now
import Quickshell.Wayland. This gives us the special tools to dock to the screen. - We use
PanelWindowinstead ofFloatingWindow. This is a window that sticks to an edge. -
anchors.top: truemeans "stick to the top of the screen." The left and right anchors make it stretch all the way across. -
implicitHeight: 30makes the bar 30 pixels tall. -
color:now sets the background color of the whole bar to a dark blue.
- We now
-
Run the new file:
qs -p ~/.config/testshell/02-bar.qmlNow you have a dark bar stuck to the top of your screen!
You know how you can have multiple virtual desktops? Let's add buttons for them.
-
Create
03-workspaces.qml. -
Copy and paste this longer code. Don't be scared—I'll explain the new parts right after.
import Quickshell import Quickshell.Wayland import Quickshell.Hyprland import QtQuick import QtQuick.Layouts PanelWindow { anchors.top: true anchors.left: true anchors.right: true implicitHeight: 30 color: "#1a1b26" RowLayout { anchors.fill: parent anchors.margins: 8 Repeater { model: 9 Text { property var ws: Hyprland.workspaces.values.find(w => w.id === index + 1) property bool isActive: Hyprland.focusedWorkspace?.id === (index + 1) text: index + 1 color: isActive ? "#0db9d7" : (ws ? "#7aa2f7" : "#444b6a") font { pixelSize: 14; bold: true } MouseArea { anchors.fill: parent onClicked: Hyprland.dispatch("workspace " + (index + 1)) } } } Item { Layout.fillWidth: true } } }
New Stuff Explained:
-
import Quickshell.Hyprland&import QtQuick.Layouts: New tools to talk to your desktop and arrange things. -
RowLayout: Tells everything inside it to line up in a row, left to right. -
Repeater: This is a COPY MACHINE.model: 9means "make 9 copies" of what's inside. We get 9 buttons (1 through 9). -
Inside the
Textfor each button:- It checks: Is this workspace number active? Does it exist?
-
color:uses that info to color it: Bright cyan if it's your current desktop, blue if it exists, gray if it's empty. -
MouseArea: This makes the number clickable! When you click it,Hyprland.dispatchtells your computer to switch to that workspace.
-
Item { Layout.fillWidth: true }: This is an invisible spacer. It pushes all the number buttons to the left side.
-
-
Run it!
qs -p ~/.config/testshell/03-workspaces.qmlTry clicking the numbers! They should switch your desktop.
Now let's make the bar useful by showing your computer's stats. We'll do this by teaching it to run a simple command (like you do in Terminal) and show the answer.
Instead of typing color codes everywhere, let's create a color palette at the top of our file. Add this inside the PanelWindow { block at the very beginning:
PanelWindow {
id: root
property color colBg: "#1a1b26"
property color colFg: "#a9b1d6"
property color colMuted: "#444b6a"
property color colCyan: "#0db9d7"
property color colBlue: "#7aa2f7"
property color colYellow: "#e0af68"
// ... the rest of your existing code ...
}Now, anywhere in the file, you can write root.colBlue instead of "#7aa2f7". Much easier!
We need to add a few things to make the CPU widget work:
-
Add the import for running commands at the very top of the file:
import Quickshell.Io -
Add these "properties" (variables to store data) near your color properties inside
PanelWindow:property int cpuUsage: 0 property int memUsage: 0 // We'll use this later for memory property var lastCpuIdle: 0 property var lastCpuTotal: 0
-
Add the CPU checking "Process" somewhere inside the
PanelWindowblock (after the properties is a good spot):Process { id: cpuProc command: ["sh", "-c", "head -1 /proc/stat"] stdout: SplitParser { onRead: data => { if (!data) return var p = data.trim().split(/\s+/) var idle = parseInt(p[4]) + parseInt(p[5]) var total = p.slice(1, 8).reduce((a, b) => a + parseInt(b), 0) if (lastCpuTotal > 0) { cpuUsage = Math.round(100 * (1 - (idle - lastCpuIdle) / (total - lastCpuTotal))) } lastCpuTotal = total lastCpuIdle = idle } } Component.onCompleted: running = true }
Don't get hung up on the math here. Think of it as a little machine that asks your computer for CPU data, does a calculation, and saves the answer as a percentage in
cpuUsage. -
Add a Timer to run the check every 2 seconds:
Timer { interval: 2000 running: true repeat: true onTriggered: cpuProc.running = true }
-
Finally, add the display widget to your
RowLayout, after the spacer (Item { Layout.fillWidth: true }) but before the closing}of the RowLayout:Text { text: "CPU: " + cpuUsage + "%" color: root.colYellow font { pixelSize: 14; bold: true } }
Let's finish our bar by adding two more widgets.
-
Add the memory-checking Process next to your CPU process:
Process { id: memProc command: ["sh", "-c", "free | grep Mem"] stdout: SplitParser { onRead: data => { if (!data) return var parts = data.trim().split(/\s+/) var total = parseInt(parts[1]) || 1 var used = parseInt(parts[2]) || 0 memUsage = Math.round(100 * used / total) } } Component.onCompleted: running = true }
-
Update your Timer to also trigger the memory check:
Timer { interval: 2000 running: true repeat: true onTriggered: { cpuProc.running = true memProc.running = true } }
Add the clock widget. This one has its own timer to update every second!
// Add a visual separator first
Rectangle { width: 1; height: 16; color: root.colMuted }
// The Memory display
Text {
text: "Mem: " + memUsage + "%"
color: root.colCyan
font { pixelSize: 14; bold: true }
}
// Another separator
Rectangle { width: 1; height: 16; color: root.colMuted }
// The Clock
Text {
id: clock
color: root.colBlue
font { pixelSize: 14; bold: true }
text: Qt.formatDateTime(new Date(), "ddd, MMM dd - HH:mm") // Format: "Wed, Feb 05 - 14:30"
Timer {
interval: 1000 // 1000 milliseconds = 1 second
running: true
repeat: true
onTriggered: clock.text = Qt.formatDateTime(new Date(), "ddd, MMM dd - HH:mm")
}
}You've just built a fully functional desktop bar piece by piece. To see the complete, final code with all these parts put together in the right order, you can view it on the original tutorial page here: Quickshell Tutorial - Build Your Own Bar. Look for the section titled "Complete Bar Example."
What to Try Now:
- Change the colors in your palette at the top.
-
Add more workspaces by changing
model: 9tomodel: 12. -
Change the clock format (the
"ddd, MMM dd - HH:mm"part). - Break it and fix it! The best way to learn is to play. Change a number, save, and see what happens when you run it again.
You've learned the basics of QML and Quickshell. You can use these same ideas—windows, layouts, timers, and processes—to build so much more, like wallpaper managers or system dashboards.
If you got value from this guide and want to see more, you can find my other tutorials on my YouTube channel or my website.
Happy building!
– Tony