Skip to content

Quickshell Beginners guide

Mattscreative edited this page Feb 5, 2026 · 2 revisions

The Complete Beginner's Guide to Building a Desktop Bar with Quickshell

1. Welcome! Let's Build Something Cool

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:

  1. Which virtual desktop you're using (you can click to switch!)
  2. How hard your computer's brain (CPU) is working
  3. How full your computer's short-term memory (RAM) is
  4. A clock that updates live

Ready? Let's go!


2. Step 1: What You Need to Start

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-git and press Enter.
    • For NixOS: You need to add quickshell to a special list in your system's configuration file. The code you need is environment.systemPackages = with pkgs; [ quickshell ];.
    • For other systems: Check the Quickshell GitHub page for instructions for your specific version.

3. Step 2: Your First Project - "Hello World!"

Let's make sure everything works by creating the simplest program possible.

  1. 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 called testshell. Your full path should be ~/.config/testshell/.
  2. Create your first code file:

    • Inside the testshell folder, create a new file named 01-hello.qml.
    • Open this file with any text editor (like Notepad, but for coding).
  3. 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: true means "show it." width and height set its size in pixels.
    • Text { }: You're putting text inside the window.
      • anchors.centerIn: parent is 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.
  4. 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!

4. Step 3: Make It a Real Bar (Not Just a Window)

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!

  1. Create a new file in the same folder called 02-bar.qml.

  2. 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 PanelWindow instead of FloatingWindow. This is a window that sticks to an edge.
    • anchors.top: true means "stick to the top of the screen." The left and right anchors make it stretch all the way across.
    • implicitHeight: 30 makes the bar 30 pixels tall.
    • color: now sets the background color of the whole bar to a dark blue.
  3. Run the new file:

    qs -p ~/.config/testshell/02-bar.qml

    Now you have a dark bar stuck to the top of your screen!


5. Step 4: Add Clickable Workspace Buttons

You know how you can have multiple virtual desktops? Let's add buttons for them.

  1. Create 03-workspaces.qml.

  2. 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: 9 means "make 9 copies" of what's inside. We get 9 buttons (1 through 9).
    • Inside the Text for 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.dispatch tells 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.
  3. Run it!

    qs -p ~/.config/testshell/03-workspaces.qml

    Try clicking the numbers! They should switch your desktop.


6. Step 5: Add System Stats (CPU & Memory)

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.

6.1 First, Let's Organize Our Colors

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!

6.2 Adding the CPU Monitor

We need to add a few things to make the CPU widget work:

  1. Add the import for running commands at the very top of the file:

    import Quickshell.Io
  2. 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
  3. Add the CPU checking "Process" somewhere inside the PanelWindow block (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.

  4. Add a Timer to run the check every 2 seconds:

    Timer {
        interval: 2000
        running: true
        repeat: true
        onTriggered: cpuProc.running = true
    }
  5. 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 }
    }

7. Step 6: Add Memory Usage and a Clock

Let's finish our bar by adding two more widgets.

7.1 Memory Monitor

  1. 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
    }
  2. Update your Timer to also trigger the memory check:
    Timer {
        interval: 2000
        running: true
        repeat: true
        onTriggered: {
            cpuProc.running = true
            memProc.running = true
        }
    }

7.2 Live Clock

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")
    }
}

8. You Did It! Next Steps & Full Code

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:

  1. Change the colors in your palette at the top.
  2. Add more workspaces by changing model: 9 to model: 12.
  3. Change the clock format (the "ddd, MMM dd - HH:mm" part).
  4. 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

Clone this wiki locally