Skip to content

Getting Started with Plugin Development

This guide will walk you through creating your first Noctalia plugin.

  • Noctalia Shell installed and running
  • Basic knowledge of QML (Qt Quick)
  • Text editor of your choice
  • Git (for submitting to the plugin registry)

Plugins are stored in ~/.config/noctalia/plugins/. You can develop directly in this directory or use a separate location and symlink it.

Terminal window
# Option 1: Develop in the plugins directory
cd ~/.config/noctalia/plugins/
mkdir my-plugin
cd my-plugin
# Option 2: Develop elsewhere and symlink
mkdir ~/dev/my-noctalia-plugin
cd ~/.config/noctalia/plugins/
ln -s ~/dev/my-noctalia-plugin my-plugin

Every plugin must have a manifest.json file. Create it with this minimal structure:

{
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"minNoctaliaVersion": "3.6.0",
"author": "Your Name",
"license": "MIT",
"repository": "https://github.com/yourusername/noctalia-plugins",
"description": "A simple example plugin",
"entryPoints": {
"barWidget": "BarWidget.qml"
},
"dependencies": {
"plugins": []
},
"metadata": {
"defaultSettings": {}
}
}

Let’s create a simple bar widget that displays an icon and text. Create BarWidget.qml:

import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Widgets
Rectangle {
id: root
// Plugin API (injected by PluginService)
property var pluginApi: null
// Required properties for bar widgets
property ShellScreen screen
property string widgetId: ""
property string section: ""
implicitWidth: row.implicitWidth + Style.marginM * 2
implicitHeight: Style.barHeight
color: Style.capsuleColor
radius: Style.radiusM
RowLayout {
id: row
anchors.centerIn: parent
spacing: Style.marginS
NIcon {
icon: "heart"
color: Color.mPrimary
}
NText {
text: "My Plugin"
color: Color.mOnSurface
pointSize: Style.fontSizeS
}
}
}

Step 4: Restart Noctalia and Enable Your Plugin

Section titled “Step 4: Restart Noctalia and Enable Your Plugin”

After creating these files, restart Noctalia:

Terminal window
# Restart Noctalia (the method depends on your setup)
# For systemd:
systemctl --user restart noctalia
# Or kill and restart manually
killall qs && qs -p ~/.config/noctalia/noctalia-shell

Then:

  1. Open Noctalia Settings (Super+comma or click the settings icon)
  2. Navigate to the Plugins tab
  3. Find your plugin in the list
  4. Click Enable
  5. Your widget should appear in the bar!

After enabling the plugin, you need to add it to the bar:

  1. Go to Settings > Bar tab
  2. Click on a section (Left/Center/Right)
  3. Find your plugin widget in the available widgets list
  4. Add it to your preferred section

Now that you have a basic plugin working, you can:

Coming Soon

  • Settings UI - Let users configure your plugin
  • Translations - Support multiple languages
  • Styling Guide - Use Noctalia’s design system

Here’s a complete example of a simple plugin that displays a counter:

manifest.json:

{
"id": "counter",
"name": "Counter Widget",
"version": "1.0.0",
"minNoctaliaVersion": "3.6.0",
"author": "Your Name",
"license": "MIT",
"repository": "https://github.com/yourusername/noctalia-plugins",
"description": "A simple counter widget",
"entryPoints": {
"barWidget": "BarWidget.qml"
},
"dependencies": {
"plugins": []
},
"metadata": {
"defaultSettings": {
"count": 0
}
}
}

BarWidget.qml:

import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Widgets
import qs.Services.UI
Rectangle {
id: root
property var pluginApi: null
property ShellScreen screen
property string widgetId: ""
property string section: ""
property int count: pluginApi?.pluginSettings?.count || 0
implicitWidth: row.implicitWidth + Style.marginM * 2
implicitHeight: Style.barHeight
color: Style.capsuleColor
radius: Style.radiusM
RowLayout {
id: row
anchors.centerIn: parent
spacing: Style.marginS
NIcon {
icon: "numbers"
color: Color.mPrimary
}
NText {
text: root.count.toString()
color: Color.mOnSurface
pointSize: Style.fontSizeM
font.weight: Font.Bold
}
}
MouseArea {
anchors.fill: parent
onClicked: {
root.count++
pluginApi.pluginSettings.count = root.count
pluginApi.saveSettings()
ToastService.showNotice("Count: " + root.count)
}
}
Component.onCompleted: {
Logger.i("Counter", "Widget loaded with count:", root.count)
}
}

This creates a counter that:

  • Displays a number icon and the current count
  • Increments when clicked
  • Saves the count to settings
  • Shows a toast notification
  • Persists across restarts

Run Noctalia from the terminal to see logs:

Terminal window
qs -p ~/.config/noctalia/noctalia-shell

Use Logger in your plugin:

Logger.i("MyPlugin", "Info message")
Logger.d("MyPlugin", "Debug message")
Logger.w("MyPlugin", "Warning message")
Logger.e("MyPlugin", "Error message")
Terminal window
# View plugin settings file
cat ~/.config/noctalia/plugins.json
# View your plugin's manifest
cat ~/.config/noctalia/plugins/my-plugin/manifest.json
# View your plugin's settings
cat ~/.config/noctalia/plugins/my-plugin/settings.json

Plugin doesn’t appear in settings:

  • Check that manifest.json is valid JSON
  • Verify the plugin ID matches the directory name
  • Restart Noctalia

Widget doesn’t show in bar:

  • Make sure you enabled the plugin in Settings > Plugins
  • Check that you added the widget in Settings > Bar
  • Look for errors in the terminal output

Settings don’t persist:

  • Make sure you call pluginApi.saveSettings() after changing settings
  • Check file permissions on ~/.config/noctalia/plugins/

Continue to learn about: