Getting Started
This page will guide you through setting up a basic development environment and creating a new extension. If this is your first extension, it is recommended that you use the gnome-extensions
tool.
GNOME Extensions Tool
TIP
The gnome-extensions create
command can be run from any directory, because it will always create the extension in ~/.local/share/gnome-shell/extensions
.
GNOME Shell ships with a program you can use to create a skeleton extension by running gnome-extensions create
.
Instead of passing options on the command line, you can start creating an extension interactively:
$ gnome-extensions create --interactive
Choose a name:
Name should be a very short (ideally descriptive) string. Examples are: “Click To Focus”, “Adblock”, “Shell Window Shrinker” Name: Example Extension
1
2
3Choose a description:
Description is a single-sentence explanation of what your extension does. Examples are: “Make windows visible on click”, “Block advertisement popups”, “Animate windows shrinking on minimize” Description: An extension serving as an example
1
2
3Choose a UUID for your extension:
UUID is a globally-unique identifier for your extension. This should be in the format of an email address (clicktofocus@janedoe.example.com) UUID: example@shell.gnome.org
1
2
3Choose the starting template:
Choose one of the available templates: 1) Plain – An empty extension 2) Indicator – Add an icon to the top bar Template [1-2]: 1
1
2
3
4
The whole process looks like this on the command line:
$ gnome-extensions create --interactive
Name should be a very short (ideally descriptive) string.
Examples are: “Click To Focus”, “Adblock”, “Shell Window Shrinker”
Name: Example Extension
Description is a single-sentence explanation of what your extension does.
Examples are: “Make windows visible on click”, “Block advertisement popups”, “Animate windows shrinking on minimize”
Description: An extension serving as an example
UUID is a globally-unique identifier for your extension.
This should be in the format of an email address (clicktofocus@janedoe.example.com)
UUID: example@shell.gnome.org
Choose one of the available templates:
1) Plain – An empty extension
2) Indicator – Add an icon to the top bar
Template [1-2]: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Once you finish the last step, the extension template will be created and opened in an editor:

Manual Creation
Start by creating an extension directory, then open the two required files in gedit
or another editor:
$ mkdir -p ~/.local/share/gnome-shell/extensions/example@shell.gnome.org
$ cd ~/.local/share/gnome-shell/extensions/example@shell.gnome.org
$ gedit extension.js metadata.json &
2
3
Populate extension.js
and metadata.json
with the basic requirements, remembering that uuid
MUST match the directory name of your extension:
metadata.json
{
"uuid": "example@shell.gnome.org",
"name": "Example",
"description": "This extension puts an icon in the panel with a simple dropdown menu.",
"version": 1,
"shell-version": [ "42" ],
"url": "https://gitlab.gnome.org/World/ShellExtensions/gnome-shell-extension-example"
}
2
3
4
5
6
7
8
extension.js
Notice that in the example below, we are using three top-level functions instead of class like in the example above.
'use strict';
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
function init() {
console.debug(`initializing ${Me.metadata.name}`);
}
function enable() {
console.debug(`enabling ${Me.metadata.name}`);
}
function disable() {
console.debug(`disabling ${Me.metadata.name}`);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Enabling the Extension
Depending on whether you are running a Wayland session or an X11 session, we will prepare a simple debugging environment. For more information about debugging GNOME Shell extensions, see the Debugging page.
For either session type, start by opening a new terminal, such as GNOME Terminal or GNOME Console.
Wayland Sessions
Execute the following command, which will start a nested instance of GNOME Shell:
dbus-run-session -- gnome-shell --nested --wayland
1Once the new process start, any output from the nested session will be printed in the same terminal.
X11 Sessions
Start by executing the following command, which will monitor the output of GNOME Shell:
$ journalctl -f -o cat /usr/bin/gnome-shell
1Then press
Alt
+F2
to open the Run a Command dialog, then run the built-in commandrestart
to have GNOME Shell load your extension.
Now that you're prepared to debug any problems with your extension, use the gnome-extensions
tool to enable your extension by running the following command:
$ gnome-extensions enable example@shell.gnome.org
In case of Wayland, execute the above command in a terminal started from within the nested Gnome Shell instance. The extension will not be visible in a shell started from your host session, unless you log out and log in first.
After this is done you should see something like the following in the log:
GNOME Shell started at Sat Aug 22 2020 07:14:35 GMT-0800 (PST)
initializing Example Extension version 1
enabling Example Extension version 1
2
3
Note that there might be lots of other logs from Gnome Shell as well, and the messages from your extension might be buried within them.
A Working Extension
As a simple example, let's add a panel button to show what a working extension might look like:
const St = imports.gi.St;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const _ = ExtensionUtils.gettext;
class Extension {
constructor() {
this._indicator = null;
}
enable() {
console.debug(`enabling ${Me.metadata.name}`);
const indicatorName = _('%s Indicator').format(Me.metadata.name);
// Create a panel button
this._indicator = new PanelMenu.Button(0.0, indicatorName, false);
// Add an icon
const icon = new St.Icon({
icon_name: 'face-laugh-symbolic',
style_class: 'system-status-icon',
});
this._indicator.add_child(icon);
// `Main.panel` is the actual panel you see at the top of the screen,
// not a class constructor.
Main.panel.addToStatusArea(indicatorName, this._indicator);
}
// REMINDER: It's required for extensions to clean up after themselves when
// they are disabled. This is required for approval during review!
disable() {
console.debug(`disabling ${Me.metadata.name}`);
this._indicator.destroy();
this._indicator = null;
}
}
function init() {
console.debug(`initializing ${Me.metadata.name}`);
ExtensionUtils.initTranslations();
return new Extension();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Now, save the extension.js
file and reload the extension, by repeating the steps in the Enabling the Extension section.
You should see a new button in the panel.