swaync(5)

swaync(5) straight outta the File Formats Manual β€” part 1 of 5, and bestie we are going to read the whole config together, no skipping πŸ’–

🌸 NAME 🌸

swaync β€” Configuration file. That's it, that's the whole name section, she's concise πŸ‘‘


βš™οΈ CONFIGURATION βš™οΈ

Okay so real talk first: when you're editing the config file, please use a text editor that has a JSON language server, because that's literally how you catch config errors before they wreck your whole vibe. Do not rawdog this in notepad, I'm begging 🫢

Every option below is written like it lives in your config, with its type, its default, its values (if it has any) and what it actually does. Let's go ✨

  • ignore-gtk-theme
    • type: bool
    • default: true
    • description: Unsets the GTK_THEME environment variable. This fixes a lot of issues where GTK themes were out here ruining the user's custom CSS themes. Iconic behaviour honestly πŸ’…
  • positionX
    • type: string
    • default: right
    • values: left, right, center
    • description: Horizontal position of the control center and the notification window. Same energy for both, no cap.
  • positionY
    • type: string
    • default: top
    • values: top, center, bottom
    • description: Vertical position of the control center and the notification window. The vertical bestie to positionX's horizontal.
  • layer
    • type: string
    • default: overlay
    • values: background, bottom, top, overlay
    • description: Layer of the notification window relative to normal windows. background is below all windows, overlay is above all windows. Pick your fighter.
  • layer-shell
    • type: bool
    • default: true
    • description: Wether or not the windows should be opened as layer-shell surfaces.
⚠️ Heads up bestie: this one Requires a swaync restart to apply. No hot reload, she needs a nap and a comeback. 😭
  • cssPriority
    • type: string
    • default: application
    • values: application, user
    • description: Which GTK priority to use when loading the default and user CSS files. Pick "user" if you want to override XDG_CONFIG_HOME/gtk-3.0/gtk.css. Main character CSS energy, you love to see it πŸ‘‘
  • control-center-positionX
    • type: string
    • default: right
    • values: left, right, center
    • description: Optional: horizontal position of the control center. Supersedes positionX if it's not set to none.
  • control-center-positionY
    • type: string
    • default: top
    • values: top, center, bottom
    • description: Optional: vertical position of the control center. Supersedes positionY if it's not set to none.
  • control-center-margin-top
    • type: integer
    • default: 0
    • description: The margin (in pixels) at the top of the notification center. 0 to disable.
  • control-center-margin-bottom
    • type: integer
    • default: 0
    • description: The margin (in pixels) at the bottom of the notification center. 0 to disable.
  • control-center-margin-right
    • type: integer
    • default: 0
    • description: The margin (in pixels) at the right of the notification center. 0 to disable.
  • control-center-margin-left
    • type: integer
    • default: 0
    • description: The margin (in pixels) at the left of the notification center. 0 to disable.
  • control-center-layer
    • type: string
    • default: top
    • values: background, bottom, top, overlay
    • description: Layer of the control center window relative to normal windows. background is below all windows, overlay is above all windows. Same scale, different window 🌈
  • control-center-exclusive-zone
    • type: bool
    • default: true
    • description: Whether or not the control center should follow the compositor's exclusive zones. A whole example: set it to false if you want it to cover your panel/dock. Rude but valid 🫢
  • notification-2fa-action
    • type: bool
    • default: true
    • description: If each notification should display a 'COPY "1234"' action. Two-factor codes, we copy you, we love you πŸŽ€
  • timeout
    • type: integer
    • default: 10
    • description: The notification timeout for notifications with normal priority.
  • timeout-low
    • type: integer
    • default: 5
    • description: The notification timeout for notifications with low priority.
  • timeout-critical
    • type: integer
    • default: 0
    • description: The notification timeout for notifications with critical priority. 0 to disable. Critical notifs get to stay, as they should πŸ’œ
  • notification-window-width
    • type: integer
    • default: 500
    • description: Width of the notification in pixels.
  • notification-window-height
    • type: integer
    • default: -1
    • description: Max height of the notification in pixels. -1 to use the full amount of space given by the compositor. Let the compositor cook πŸ“
  • notification-window-preferred-output
    • type: string
    • default: ""
    • description: The preferred output to open the notification window (popup notifications) on. Can either be the monitor connector name (ex: "DP-1"), or the full name, manufacturer model serial (ex: "Acer Technologies XV272U V 503023B314202"). If the output is not found, the currently focused one is picked. She'll find her way home πŸ’«
  • keyboard-shortcuts
    • type: bool
    • default: true
    • description: If the control center should use keyboard shortcuts.
  • notification-grouping
    • type: bool
    • default: true
    • description: If notifications should be grouped by app name. Organization is self care 🧚
  • image-visibility
    • type: string
    • default: always
    • values: always, when-available, never
    • description: An explanation about the purpose of this instance. (Yes, that's literally what the docs say for this one. We keep it verbatim, bestie, that's the rule πŸ˜­πŸ’€)
  • transition-time
    • type: integer
    • default: 200
    • description: The notification animation duration. 0 to disable. Motion is cute, but so is instant, you do you ✨
  • hide-on-clear
    • type: bool
    • default: false
    • description: Hides the control center after pressing "Clear All". Bye bye panel πŸ‘‹
  • hide-on-action
    • type: bool
    • default: true
    • description: Hides the control center when clicking on a notification action. Handled it, out of my sight πŸ’…
  • text-empty
    • type: string
    • default: "No Notifications"
    • description: Text that appears when there are no notifications to show. The quiet era 🌸
  • fit-to-screen
    • type: bool
    • default: true
    • description: Whether the control center should expand vertically to fill the screen.
  • relative-timestamps
    • type: bool
    • default: true
    • description: Display notification timestamps relative to now, e.g. "26 minutes ago". If false, a local iso8601-formatted absolute timestamp is displayed. Choose your timeline aesthetic ⏰
  • control-center-height
    • type: integer
    • default: 500
    • description: Height of the control center in pixels.
      • A value of -1 means that it will fit to the content.
      • Ignored when 'fit-to-screen' is set to 'true'.
      • Also limited to the height of the monitor, unless 'layer-shell-cover-screen' is set to false.
  • control-center-width
    • type: integer
    • default: 500
    • description: The control center width in pixels.
  • control-center-preferred-output
    • type: string
    • default: ""
    • description: The preferred output to open the control center on. Can either be the monitor connector name (ex: "DP-1"), or the full name, manufacturer model serial (ex: "Acer Technologies XV272U V 503023B314202"). If the output is not found, the currently focused one is picked. Same rules as the notification one, different window πŸ’–
  • notification-action-filter
    • type: object
    • visibility object properties:
      • use-regex
        • type: bool
        • optional: true
        • default: false
        • description: Indicates if all the below fields should use regex or not.
      • app-name
        • type: string
        • optional: true
        • description: The app-name.
      • desktop-entry
        • type: string
        • optional: true
        • description: The desktop-entry.
      • id-matcher
        • type: string
        • optional: true (needs at least one matcher)
        • description: Matches the action's identifier. Can be found by reading the output of swaync when run with the G_MESSAGES_DEBUG=all environment variable.
      • text-matcher
        • type: string
        • optional: true (needs at least one matcher)
        • description: Matches the action's text visible in the notification.
    • description: Hides matching action(s) of matching notifications. If the notification doesn't include one of the properties, that property will be ignored. If all properties match the given notification, the matching actions will be hidden.
    • example:
{
     "notification-action-filter": {
          "hide-chromium-settings": {
               "desktop-entry": "chromium-browser",
               "use-regex": false,
               "id-matcher": "settings",
               "text-matcher": "Settings"
          }
     },
}

And that's a wrap on the config options, but bestie we are NOT done β€” this was only part 1 of 5. Stay tuned, there's more page to slay πŸ’–βœ¨

🌸 notification-visibility 🌸

ok so this one is type: object, and every key inside it is a visibility object with these properties bestie, listen up:

  • state β€” type: string, optional: false (so you literally have to give it), default: enabled, values: ignored, muted, transient, enabled. it's the notification visibility state πŸ’…
  • override-urgency β€” type: string, optional: true, default: unset, values: unset, low, normal, critical. this is the new urgency for the notification if it's set ✨
  • app-name β€” type: string, optional: true. the app-name. uses Regex 🧐
  • desktop-entry β€” type: string, optional: true. the desktop-entry. uses Regex 🧐
  • summary β€” type: string, optional: true. the summary of the notification. uses Regex 🧐
  • body β€” type: string, optional: true. the body of the notification. uses Regex 🧐
  • urgency β€” type: string, optional: true, default: Normal, values: Low, Normal, Critical. the urgency of the notification 🚨
  • category β€” type: string, optional: true. which category the notification belongs to. uses Regex 🧐

description: set the visibility or override the urgency of each incoming notification. if the notification doesn't include one of the properties, that property gets ignored (no hard feelings, it just skips it). all properties β€” except for state β€” use regex. if all the properties match the given notification, then the notification will follow the provided state. and bestie, pay attention: only the first matching object will be used. so order matters, periodt πŸ’…

example:

{
     "notification-visibility": {
          "example-name": {
               "state": "The notification state",
               "app-name": "Notification app-name Regex",
               "summary": "Notification summary Regex",
               "body": "Notification body Regex",
               "urgency": "Low or Normal or Critical",
               "category": "Notification category Regex"
          }
     }
}

πŸŽ€ widgets πŸŽ€

type: array. the default values are literally ["title", "dnd", "notifications"] and honestly, iconic lineup πŸ’–

valid array values (peep widget-config for the whole tea):

  • notifications β€” required: true, optional: false (she is NOT optional, she is the moment πŸ‘‘)
  • title β€” optional: true
  • dnd β€” optional: true
  • label β€” optional: true
  • mpris β€” optional: true
  • menubar β€” optional: true
  • buttons-grid β€” optional: true
  • slider β€” optional: true
  • volume β€” optional: true
  • backlight β€” optional: true
  • inhibitors β€” optional: true

description:

which order and which widgets to display. simple as that πŸ¦‹

if the "notifications" widget isn't specified, it will be placed at the bottom. so like, she shows up anyway, she just shows up last ✨

multiple of same widget:

ok this is the part where it gets cute. append a # with any value to the end of the widget name. example: "title#TheMainTitle" πŸŽ€

and to address this widget specifically in the css file, use the css class .TheMainTitle πŸ’…

example:

{
     "widgets": [
          "inhibitors",
          "title",
          "dnd",
          "notifications"
     ]
}

🧁 widget-config β€” the part where u make each widget yours

type: object

description: Configure specific widget properties.

Like. This is the section where u stop treating ur notification daemon as one big blob and start whispering to individual widgets about how u want them to behave. Very main character energy. πŸ’…βœ¨


πŸ” got multiple of the same widget??

okay bestie this is the trick that unlocks everything:

  • Append a # with any value to the end of the widget name.
  • Example: "title#TheMainTitle"
  • To address this widget specifically in the css file use the css class .TheMainTitle

So u can have a title and a title#TheMainTitle and style them totally separately. iconic. πŸŽ€


🎁 Widgets to customize

πŸ”” notifications

  • type: object
  • css class: widget-notifications
  • properties:
    • vexpand β€” type: bool, optional: true, default: true β€” Whether or not the notifications widget should vertically expand or not
  • description: The Notifications Widget.

🏷️ title

  • type: object
  • css class: widget-title
  • properties:
    • text β€” type: string, optional: true, default: "Notifications" β€” The title of the widget
    • clear-all-button β€” type: bool, optional: true, default: true β€” Whether to display a "Clear All" button
    • button-text β€” type: string, optional: true, default: "Clear All" β€” "Clear All" button text
  • description: The notification visibility state.

πŸŒ™ dnd

  • type: object
  • css class: widget-dnd
  • properties:
    • text β€” type: string, optional: true, default: "Do Not Disturb" β€” The title of the widget
  • description: Control Center Do Not Disturb Widget.

πŸ“ label

  • type: object
  • css class: widget-label
  • properties:
    • text β€” type: string, optional: true, default: "Label Text" β€” The text content of the widget
    • clear-all-button β€” type: integer, optional: true, default: 5 β€” The maximum lines
yes bestie that one is an integer and yes it's named clear-all-button. we don't make the rules, we just report them faithfully. 🫠
  • description: A generic widget that allows the user to add custom text.

🎡 mpris

  • type: object
  • css classes:
    • widget-mpris
    • widget-mpris-player
    • widget-mpris-title
    • widget-mpris-subtitle
  • properties:
    • blacklist β€” type: array, optional: true, default: [] β€” Audio sources for the mpris widget to ignore.
      • Valid array values:
        • type: string
        • description: Audio source/app name. Regex allowed. Hint $ qdbus | grep mpris to find source names.
    • autohide β€” type: bool, optional: true, default: false β€” Whether to hide the widget when the player has no metadata.
    • show-album-art β€” type: string, optional: true, default: "always" β€” Whether or not the album art should be hidden, always visible, or only visible when a valid album art is provided.
      • enum: ["right", "left"]
    • loop-carousel β€” type: bool, optional: true, default: false β€” Whether to loop through the mpris carousel.
  • description: A widget that displays multiple music players.
not me staring at that enum: ["right", "left"] under show-album-art πŸ‘€ that's exactly what the page says, so that's exactly what u get. no notes. πŸ’€
  • type: object
  • css classes:
    • widget-menubar
    • .widget-menubar>box>.menu-button-bar
    • name of element given after menu or buttons with #
  • patternProperties:
    • menu#<name>
      • type: object
      • properties:
        • label β€” type: string, optional: true, default: "Menu" β€” Label of button to show/hide menu dropdown
        • position β€” type: string, optional: true, default: "right" β€” Horizontal position of the button in the bar
          • enum: ["right", "left"]
        • animation-type β€” type: string, optional: true, default: "slide_down" β€” Animation type for menu
          • enum: ["slide_down", "slide_up", "none"]
        • animation-duration β€” type: integer, optional: true, default: 250 β€” Duration of animation in milliseconds
        • actions β€” type: array, Default values: [] β€” A list of actions containing a label and a command
          • Valid array values:
            • type: object
            • properties:
              • label β€” type: string, default: "label" β€” Text to be displayed in button
              • command β€” type: string, default: "" β€” "Command to be executed on click"
              • type β€” type: string, default: "normal" β€” Type of the button. Toggle buttons receive the '.active' css class
              • enum: ["normal", "toggle"]
              • update-command β€” type: string, default: "" β€” "Command to be executed on visibility change of cc to update the active state of the toggle button (should echo true or false)"
              • active β€” type: bool, default: false β€” Wether the toggle button is active as default or not
      • description: A button to reveal a dropdown with action-buttons
    • buttons#<name>
      • type: object
      • properties:
        • position β€” type: string, optional: true, default: "right" β€” Horizontal position of the buttons in the bar
          • enum: ["right", "left"]
        • actions β€” type: array, Default values: []
          • Valid array values:
            • type: object
            • properties:
              • label β€” type: string, default: "label" β€” Text to be displayed in button
              • command β€” type: string, default: "" β€” "Command to be executed on click"
              • type β€” type: string, default: "normal" β€” Type of the button. Toggle buttons receive the '.active' css class and an env variable "SWAYNC_TOGGLE_STATE" is set. See example usage in the default config.json
              • enum: ["normal", "toggle"]
              • update-command β€”

okay so continuing the widget tour, bestie πŸ’– β€” the previous widget (you know the one) wraps up with these final properties:

  • type β€” type: string; default: ""; description: "Command to be executed on visibility change of cc to update the active state of the toggle button (should echo true or false)" yes that's a whole sentence, slay
  • active β€” type: bool; default: false; description: Wether the toggle button is active as default or not (note: "Wether" is the original spelling, iykyk)
  • description: A list of actions containing a label and a command
  • description: A list of buttons to be displayed in the menu-button-bar

✨ buttons-grid ✨

this one is giving a grid of buttons that execute shell commands πŸ”₯

  • type β€” type: object
  • css class β€” widget-buttons (access buttons with >flowbox>flowboxchild>button)
  • buttons-per-row β€” type: number
  • actions β€” type: array; Default values: []; description: A list of actions containing a label and a command; Valid array values:
    • type: object
    • properties:
      • label β€” type: string; default: "label"; description: Text to be displayed in button
      • command β€” type: string; default: ""; description: "Command to be executed on click"
      • type β€” type: string; default: "normal"; description: Type of the button Toggle buttons receive the '.active' css class and an env variable "SWAYNC_TOGGLE_STATE" is set. See example usage in the default config.json; enum: ["normal", "toggle"]
      • active β€” type: bool; default: false; description: Wether the toggle button is active as default or not
  • description β€” description: A grid of buttons that execute shell commands

🎚️ slider 🎚️

general slider control, periodt πŸ’…

  • type β€” type: object
  • css class β€” widget-slider
  • label β€” type: string; optional: true; default: "slider"; description: Text displayed in front of the slider
  • cmd_setter β€” type: string; optional: true; default: ""; description: command to set the value. Use $value to get the current value.
  • cmd_getter β€” type: string; optional: true; default: ""; description: command to get the actual value. Use $value to get the current value.
  • min β€” type: integer; optional: true; default: 0; description: minimum value of the slider range
  • max β€” type: integer; optional: true; default: 100; description: maximum value of the slider range
  • min_limit β€” type: integer; optional: true; default: 0; description: limit minimum value of the slider
  • max_limit β€” type: integer; optional: true; default: 100; description: limit maximum value of the slider
  • value_scale β€” type: integer; optional: true; default: 0; description: scale small value, slider round digits
  • description β€” description: general slider control

πŸ”Š volume πŸ”Š

slider to control pulse volume, no cap 🎢

  • type β€” type: object
  • css class β€” widget-volume, per-app-volume
  • label β€” type: string; optional: true; default: "Volume"; description: Text displayed in front of the volume slider
  • show-per-app β€” type: bool; optional: true; default: false; description: Show per app volume control
  • show-per-app-icon β€” type: bool; optional: true; default: false; description: Show application icon in per app control
  • show-per-app-label β€” type: bool; optional: true; default: false; description: Show application name in per app control
  • expand-per-app β€” type: bool; optional: true; default: false; description: If the per app section should start expanded
  • empty-list-label β€” type: string; optional: true; default: "No active sink input"; description: Text displayed when there are not active sink inputs
  • expand-button-label β€” type: string; optional: true; default: "⇧"; description: Label displayed on button to show per app volume control
  • collapse-button-label β€” type: string; optional: true; default: "⇩"; description: Label displayed on button to hide per app volume control
  • animation-type β€” type: string; optional: true; default: "slide_down"; description: Animation type for the per app volume control; enum: ["slide_down", "slide_up", "none"]
  • animation-duration β€” type: integer; optional: true; default: 250; description: Duration of animation in milliseconds
  • description β€” description: Slider to control pulse volume

πŸ’‘ backlight πŸ’‘

slider to control screen brightness, main character energy ✨

  • type β€” type: object
  • css class β€” widget-backlight
  • label β€” type: string; optional: true; default: "Brightness"; description: Text displayed in front of the backlight slider
  • device β€” type: string; optional: true; default: "intel_backlight"; description: Device in /sys/class/backlight or /sys/class/leds
  • subsystem β€” type: string; optional: true; default: "backlight"; description: Kernel subsystem for brightness control; enum: ["backlight", "leds"]
  • min β€” type: integer; optional: true; default: 0; description: Lowest possible value for brightness
  • description β€” description: Slider to control screen brightness

🚫 inhibitors 🚫

displayed if notifications are inhibited, iykyk πŸ’€

  • type β€” type: object
  • css class β€” widget-inhibitors
  • text β€” type: string; optional: true; default: "Inhibitors"; description: The title of the widget
  • clear-all-button β€” type: bool; optional: true; default: true; description: Whether to display a "Clear All" button
  • button-text β€” type: string; optional: true; default: "Clear All"; description: "Clear All" button text
  • description β€” description: Displayed if notifications are inhibited.
{
     "widget-config": {
          "title": {
               "text": "Notifications",
               "clear-all-button": true,
               "button-text": "Clear All"
          },
          "dnd": {
               "text": "Do Not Disturb"
          },
          "label": {
               "max-lines": 5,
               "text": "Label Text"
          },
          "mpris": {
               "blacklist": ["playerctld"],
               "autohide": true,
               "show-album-art": "always",
               "loop-carousel": false
          },
          "menubar": {
               "menu#power": {
                    "label": "Power",
                    "position": "right",
                    "actions": [
                         {
                              "label": "Shut down",
                              "command": "systemctl poweroff"
                         },
                         ...
                    ]
               },
               "buttons#screenshot": {
                    "position": "left",
                    "actions": [
                         {
                              "label": "Screenshot",
                              "command": "grim"
                         },
                         ...
                    ]
               }
          },
          "buttons": {
               "actions": [
                    {
                         "label": "wifi",
                         "command": "rofi-wifi-menu"
                    },
                    ...
               ]
          }
     }
}

πŸŽ€ Scripts πŸŽ€

Bestie. Okay. This part is literally my favorite, because it's how you make swaync do things when notifications roll in. It's giving automation queen energy. πŸ’…

  • script-fail-notify β€” type: bool, default: true. If a script just... does not run? Yeah, you get a notification about it. Keeping you in the loop fr fr. πŸ™ˆ
  • scripts β€” type: object. The properties of a script object, babes:
    • exec β€” type: string, optional: false. The script to run. You can also run regular shell commands here, no cap.
    • app-name β€” type: string, optional: true. The app-name. Uses Regex.
    • desktop-entry β€” type: string, optional: true. The desktop-entry. Uses Regex.
    • summary β€” type: string, optional: true. The summary of the notification. Uses Regex.
    • body β€” type: string, optional: true. The body of the notification. Uses Regex.
    • urgency β€” type: string, optional: true, default: Normal, values: Low, Normal, Critical. The urgency of the notification. 🚨
    • category β€” type: string, optional: true. Which category the notification belongs to. Uses Regex.
    • sound-file β€” type: string, optional: true. Which sound file the notification requested. Uses Regex.
    • sound-name β€” type: string, optional: true. Which sound name the notification requested. Uses Regex.
    • run-on β€” type: string, optional: true, values: action, receive, default: receive. Whether to run this action when the notification is received, or when an action is taken on it.
  • description: which scripts to check and potentially run for every notification. If the notification doesn't include one of the properties, that property will be ignored. All properties (except for exec) use regex. If all properties match the given notification, the script will be run. Only the first matching script will be run. πŸ‘‘
  • example:
{
     "scripts": {
          "example-script": {
               "exec": "Your shell command or script here...",
               "app-name": "Notification app-name Regex",
               "summary": "Notification summary Regex",
               "body": "Notification body Regex",
               "urgency": "Low or Normal or Critical",
               "category": "Notification category Regex"
          }
     }
}

You can also use these environment variables in your script, bestie β€” they're just handed to you, no setup required:

$SWAYNC_BODY="Notification body content"
$SWAYNC_DESKTOP_ENTRY="Desktop entry"
$SWAYNC_URGENCY="Notification urgency"
$SWAYNC_TIME="Notification time"
$SWAYNC_APP_NAME="Notification app name"
$SWAYNC_CATEGORY="SwayNC notification category"
$SWAYNC_REPLACES_ID="ID of notification to replace"
$SWAYNC_ID="SwayNC notification ID"
$SWAYNC_SUMMARY="Notification summary"
$SWAYNC_HINT_[NAME]="Value of the hint [NAME]"
$SWAYNC_SOUND_NAME="The name of the requested sound"
$SWAYNC_SOUND_FILE="The file path of the requested sound"

And that's a wrap on swaync(5)! πŸ¦‹ This little icon of a page comes from the swaync source, dated 2026-05-19. Go forth and configure, you gorgeous genius β€” your notifications have never looked this good. βœ¨πŸ’–πŸŽ€

πŸ“¦ where this page comes from