Now that your script loads properly, let’s create a user interface where users can interact with your script. The WME SDK provides several UI locations, but the sidebar tab is the most common and user-friendly option.
Now let’s create your first sidebar tab using the SDK’s registerScriptTab method.
First, modify the init() method to register your sidebar tab and create an empty main() function:
function init() {
console.log(`${SCRIPT_NAME}: Script initialized successfully!`);
// Build HTML content for our tab
const section = document.createElement('div');
section.innerHTML = `
<div>
<h2>Our First Script!</h2>
</div>
`;
// Register the script tab using the SDK
wmeSDK.Sidebar.registerScriptTab()
.then(({ tabLabel, tabPane }) => {
tabLabel.textContent = 'WazeDev';
tabLabel.title = SCRIPT_NAME;
tabPane.appendChild(section);
// Initialize the main() function after tab is created
main();
})
.catch((error) => {
console.error(`${SCRIPT_NAME}: Error creating script tab`, error);
});
}
function main() {
// Settings initialization code will go here
console.log(`${SCRIPT_NAME}: Settings initialized!`);
}
const section = document.createElement('div');
section.innerHTML = `
<div>
<h2>Our First Script!</h2>
</div>
`;
This creates our tab’s content using a template literal (notice the backticks ` ). Template literals allow multi-line strings and variable interpolation, making HTML creation cleaner than string concatenation.
wmeSDK.Sidebar.registerScriptTab()
.then(({ tabLabel, tabPane }) => {
// Success: Tab created successfully
})
.catch((error) => {
// Error: Something went wrong
});
Why use Promises here? Tab creation involves DOM manipulation and potential conflicts with other scripts. The Promise pattern ensures your script handles both success and failure scenarios gracefully.
When registerScriptTab() succeeds, it provides two DOM elements through destructuring assignment:
tabLabel - The clickable tab button in the sidebar
tabLabel.textContent = 'WazeDev'tabLabel.title = SCRIPT_NAMEtabPane.appendChild(section)There are multiple ways to define the HTML for the page - you are not locked into doing it the same as the above example, it is just a way to define the tab HTML.
While we used createElement() and innerHTML, you have several options:
Method 1: Template Literals (Our Example)
const section = document.createElement('div');
section.innerHTML = `<div><h2>Our First Script!</h2></div>`;
Best for: Complex HTML with multiple elements
Method 2: Pure DOM Methods
const section = document.createElement('div');
const wrapper = document.createElement('div');
const header = document.createElement('h2');
header.textContent = 'Our First Script!';
wrapper.appendChild(header);
section.appendChild(wrapper);
Best for: Simple structures or when you need element references
Method 3: Document Fragments
const fragment = document.createDocumentFragment();
// Build multiple elements, then append fragment
Best for: Performance-critical scenarios with many elements
Method 4: jQuery
const $section = $('<div>').html(`
<div>
<h2>Our First Script!</h2>
<p>Built with jQuery!</p>
</div>
`);
tabPane.appendChild($section[0]); // Convert jQuery object to DOM element
Best for: Developers familiar with jQuery syntax (shorter, more readable code)
In the future choose the method that feels most comfortable for your coding style. But we will use Method 1: Template Literals in this exsample.
This example demonstrates several important WME scripting concepts:
These patterns will serve as the foundation for more complex script features.
Your tab should look like the following:

After saving and reloading WME:
WazeDev First Script: Settings initialized!Troubleshooting:
registerScriptTab() calltabPane.appendChild(section) executed successfully💡 Tip: Use your browser’s Element Inspector (F12 → Elements) to examine the generated HTML structure and debug styling issues.
// ==UserScript==
// @name WazeDev First Script // Display name in Tampermonkey dashboard
// @namespace http://tampermonkey.net/ // Unique identifier (use your GreasyFork profile for updates)
// @version 0.1 // Version number for update tracking
// @description Learning to script! // Brief description of functionality
// @author You // Your name or username
// @include https://beta.waze.com/* // Run on beta WME (testing environment)
// @include https://www.waze.com/editor* // Run on production WME
// @include https://www.waze.com/*/editor* // Run on localized WME URLs
// @exclude https://www.waze.com/user/editor* // Don't run on user profile pages
// @exclude https://www.waze.com/editor/sdk/* // Don't run on SDK documentation
// @grant none // No special permissions needed
// ==/UserScript==
(function() {
'use strict';
const SCRIPT_NAME = GM_info.script.name; // Get the Script name from the @name tag of the UserScript Meta Keys
let wmeSDK; // Declare wmeSDK globally
if (window.SDK_INITIALIZED) {
console.log(`${SCRIPT_NAME}: SDK initialized...`);
window.SDK_INITIALIZED.then(bootstrap).catch((err) => {
console.error(`${SCRIPT_NAME}: SDK initialization failed`, err);
});
} else {
console.warn(`${SCRIPT_NAME}: SDK_INITIALIZED is undefined`);
}
function bootstrap() {
try {
wmeSDK = getWmeSdk({
scriptId: SCRIPT_NAME.replaceAll(' ', ''),
scriptName: SCRIPT_NAME,
});
Promise.all([wmeReady()])
.then(() => {
console.log(`${SCRIPT_NAME}: All dependencies are ready.`);
init();
})
.catch((error) => {
console.error(`${SCRIPT_NAME}: Error during bootstrap`, error);
});
} catch (error) {
console.error(`${SCRIPT_NAME}: Failed to initialize SDK`, error);
}
}
function wmeReady() {
return new Promise((resolve) => {
if (wmeSDK.State.isReady()) {
console.log(`${SCRIPT_NAME}: WME is ready.`);
resolve();
} else {
console.log(`${SCRIPT_NAME}: Waiting for WME to be ready...`);
wmeSDK.Events.once({ eventName: 'wme-ready' })
.then(() => {
console.log(`${SCRIPT_NAME}: WME is ready now.`);
resolve();
})
.catch((error) => {
console.error(`${SCRIPT_NAME}: Error while waiting for WME to be ready:`, error);
});
}
});
}
function init() {
console.log(`${SCRIPT_NAME}: Script initialized successfully!`);
// Build HTML content for our tab
const section = document.createElement('div');
section.innerHTML = `
<div>
<h2>Our First Script!</h2>
</div>
`;
// Register the script tab using the SDK
wmeSDK.Sidebar.registerScriptTab()
.then(({ tabLabel, tabPane }) => {
tabLabel.textContent = 'WazeDev';
tabLabel.title = SCRIPT_NAME;
tabPane.appendChild(section);
// Initialize the main() function after tab is created
main();
})
.catch((error) => {
console.error(`${SCRIPT_NAME}: Error creating script tab`, error);
});
}
function main() {
// Settings initialization code will go here
console.log(`${SCRIPT_NAME}: Settings initialized!`);
}
})();
In the next section we will start interacting with the Waze SDK and display some data in our tab.