# What Kinds of Plugins Can a Quickshell Plugin Declare? A Complete Guide to Quickshell Plugin Types

> Discover the six Quickshell plugin kinds: bar, bar-widget, panel, overlay, menu, and service. Learn how these types define plugin behavior and UI placement in this complete guide.

- Repository: [37signals/omarchy](https://github.com/basecamp/omarchy)
- Tags: deep-dive
- Published: 2026-08-24

---

**Quickshell plugins declare a `kinds` array in their [`manifest.json`](https://github.com/basecamp/omarchy/blob/main/manifest.json) file to specify their type, with six supported values—`bar`, `bar-widget`, `panel`, `overlay`, `menu`, and `service`—that determine how the shell loads the plugin and where it appears in the UI.**

Quickshell, the extensible shell framework from the **basecamp/omarchy** repository, uses a declarative manifest system where each plugin must announce its capabilities through the `kinds` field. The `PluginRegistry.validateManifest` function validates this array during installation, and the shell core uses these entries to route the plugin to the correct UI container or background service loader.

## The Six Quickshell Plugin Kinds

Every Quickshell plugin must declare at least one kind from the supported set. Each kind triggers distinct loading behavior in `shell/shell.qml` and determines whether the plugin renders as a visual component or runs as a background process.

### bar

The **`bar`** kind designates a complete bar layout option. When a plugin declares this kind, the shell treats it as a candidate for the active bar configuration.

According to the source code in `shell/services/PluginRegistry.qml` (lines 28–34), the shell compares the plugin ID against `config.bar.id` to determine whether this bar plugin should be instantiated. Only one bar plugin can be active at a time, and the selection is controlled by the user's configuration.

### bar-widget

The **`bar-widget`** kind indicates a component that lives inside the bar’s layout. Unlike the `bar` kind, which replaces the entire bar, widgets occupy specific sections.

As implemented in `shell/shell.qml` (lines 659–675), the shell inserts enabled bar widgets into `config.bar.layout.left`, `config.bar.layout.center`, or `config.bar.layout.right` based on the manifest’s `defaultSection` property or explicit placement configuration. The registry validates these entries before the shell creates their QML components.

### panel

The **`panel`** kind represents a traditional floating window or dock-style interface. Panels are added to the top-level `config.plugins[]` array and loaded through the generic panel loader.

The implementation in `shell/shell.qml` (lines 598–603) shows that panels are instantiated as distinct windows that users can position and manage independently of the main bar.

### overlay

The **`overlay`** kind creates fullscreen or semi-transparent UI layers that appear above other windows. Overlays share the same `config.plugins[]` storage as panels but are distinguished by their kind during initialization.

The shell handles overlays in `shell/shell.qml` (lines 589–595), where it checks for the `overlay` kind to apply appropriate window flags and stacking behavior, ensuring the UI appears above normal application windows.

### menu

The **`menu`** kind defines launcher-style interfaces, such as dmenu or application menu implementations. Like panels and overlays, menus are stored in `config.plugins[]`.

The loading logic in `shell/shell.qml` (lines 589–595) processes menus alongside overlays, though they are typically triggered by specific keybindings or bar buttons rather than appearing as persistent windows.

### service

The **`service`** kind declares a background process that provides functionality without UI components. Services do not appear in the bar or plugins list.

As noted in `shell/shell.qml` (lines 265–291), the shell loads services through a dedicated service loader that instantiates the QML component but never adds it to a visual layout. These plugins run silently to provide APIs, data sources, or system integration for other UI components.

## How PluginRegistry Validates Plugin Kinds

The `PluginRegistry.validateManifest` function in `shell/services/PluginRegistry.qml` (lines 64–66) enforces that every manifest contains a valid `kinds` array. The registry ensures that each entry matches one of the six supported types and that the manifest includes corresponding `entryPoints` for each declared kind.

If validation passes, the registry stores the plugin metadata and exposes helper methods like `entryPointUrl(manifest, kind)`, which the shell uses to resolve the correct QML file for each plugin type.

## Implementing Each Kind in Your Manifest

### Declaring Multiple Kinds

Plugins can combine kinds when they provide both UI and background functionality. A manifest declaring a panel and a service resembles:

```json
{
  "schemaVersion": 1,
  "id": "example.panel-service",
  "name": "Example Panel + Service",
  "version": "1.0.0",
  "kinds": ["panel", "service"],
  "entryPoints": {
    "panel": "Panel.qml",
    "service": "Service.qml"
  }
}

```

### Declaring Bar Widgets

Bar widgets require placement hints and a single entry point:

```json
{
  "schemaVersion": 1,
  "id": "example.clock-widget",
  "name": "Clock Widget",
  "version": "1.0.0",
  "kinds": ["bar-widget"],
  "entryPoints": {
    "bar-widget": "BarWidget.qml"
  },
  "barWidget": {
    "defaultSection": "right"
  }
}

```

### Loading Services Programmatically

When the shell instantiates services, it checks the kinds array before creating the component:

```qml
// From shell/shell.qml – generic service loader
if (Array.isArray(manifest.kinds) && manifest.kinds.indexOf("service") !== -1) {
    var url = pluginRegistry.entryPointUrl(manifest, "service")
    // Create and start the service component
}

```

### Inserting Bar Widgets into the Layout

The shell dynamically constructs the bar layout by splicing enabled widgets into the configuration:

```qml
// From shell/shell.qml – bar widget placement logic
if (isBarWidget && !location.found) {
    var section = defaultBarWidgetSection(manifest)
    var target = barTarget(config, placement || {}, section)
    config.bar.layout[target.section].splice(target.index, 0, { id: key })
}

```

## Summary

- **Six distinct kinds** define Quickshell plugin capabilities: `bar`, `bar-widget`, `panel`, `overlay`, `menu`, and `service`.
- **`PluginRegistry.validateManifest`** in `shell/services/PluginRegistry.qml` enforces valid kind declarations during plugin installation.
- **Bar plugins** compete for the single active bar slot via `config.bar.id` comparison.
- **Bar widgets** inject into specific layout sections (left, center, right) of the active bar.
- **Panels, overlays, and menus** populate the `config.plugins[]` array but receive different window treatments based on their kind.
- **Services** run headless through a dedicated loader and never appear in the visual hierarchy.

## Frequently Asked Questions

### Can a single Quickshell plugin declare multiple kinds?

Yes. A plugin can declare multiple kinds in its [`manifest.json`](https://github.com/basecamp/omarchy/blob/main/manifest.json) array, such as `["panel", "service"]` to provide both a UI panel and a background service. The shell loads each kind independently using the corresponding entry point defined in the `entryPoints` object.

### How does the shell decide which bar plugin to activate?

The shell compares the plugin ID against `config.bar.id` as implemented in `shell/services/PluginRegistry.qml` (lines 28–34). Only the plugin whose ID matches the configuration value becomes the active bar; other `bar` kind plugins remain dormant.

### What is the difference between panel and overlay kinds?

Both store in `config.plugins[]`, but the shell applies distinct window management in `shell/shell.qml` (lines 589–603). **Panels** typically appear as persistent dock windows, while **overlays** receive window flags that keep them above other applications, making them suitable for fullscreen launchers or system dashboards.

### Do service plugins require entry points?

Yes. Even though services lack UI, they must specify a QML file in `entryPoints.service`. The shell loads this component via `pluginRegistry.entryPointUrl()` and instantiates it without adding it to any visual layout, as handled in `shell/shell.qml` (lines 265–291).