Add OneSignal Web Push to your website using Google Tag Manager (GTM), including service worker setup, initialization, and sending user tags safely.
This guide shows you how to load and initialize the OneSignal Web SDK using Google Tag Manager (GTM), then optionally set an External ID and send OneSignal Tags after initialization.
Create GTM variables for values you reference across tags. This avoids hardcoding and makes your setup easier to maintain.Create a ONESIGNAL_APP_ID variable
In GTM, go to Variables > New.
Choose Constant.
Name it ONESIGNAL_APP_ID
Set the value to your OneSignal App ID.
Save
You can now reference your App ID anywhere in GTM using {{ ONESIGNAL_APP_ID }}.
Create an ONESIGNAL_EXTERNAL_ID variable (Recommended)Use this if you associate users with an external identifier (for example, a user ID from your database or auth system).Choose a variable type based on where the value lives on your site. Common options:
Under Advanced Settings > Tag firing options, set Once per page.
Under Triggering, select Initialization - All Pages.
HTML
Copy
Ask AI
<!-- OneSignal – Web SDK initialization using Google Tag Manager This snippet: - Loads the OneSignal Web SDK - Initializes OneSignal with your App ID - Enables the Subscription Bell (notifyButton) Works for most sites out of the box.--><!-- 1. Load the OneSignal Web SDK (v16) --><!-- This script must load on every page where you want OneSignal available --><script src="https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js" defer></script><script> // Ensure the GTM dataLayer exists // Used here only to optionally push a "OneSignalInitialized" event window.dataLayer = window.dataLayer || []; // OneSignalDeferred is a queue that runs once the SDK is fully loaded window.OneSignalDeferred = window.OneSignalDeferred || []; // 2. Initialize OneSignal once the SDK is ready window.OneSignalDeferred.push(function (OneSignal) { OneSignal.init({ /* REQUIRED It is recommended to set the OneSignal App ID as a GTM variable. You can find this in your OneSignal Dashboard under: Settings > Keys & IDs */ appId: "{{ONESIGNAL_APP_ID}}", /* OPTIONAL – ONLY NEEDED IF YOUR SERVICE WORKER IS NOT AT THE ROOT If your service worker is hosted at: /OneSignalSDKWorker.js …then you should NOT set serviceWorkerPath or serviceWorkerParam. Uncomment and update the options below ONLY if your service worker is hosted in a subdirectory (for example: /push/onesignal/). */ //serviceWorkerPath: "push/onesignal/OneSignalSDKWorker.js", //serviceWorkerParam: { scope: "/push/onesignal/" }, /* OPTIONAL Enable the OneSignal Subscription Bell (notifyButton), which allows users to subscribe or unsubscribe from notifications. For more prompt options, see: https://documentation.onesignal.com/docs/en/permission-requests */ notifyButton: { enable: true } }) .then(function () { // OneSignal initialized successfully console.log("[OneSignal] init success"); // Recommended: push an event to GTM for triggering other tags window.dataLayer.push({ event: "OneSignalInitialized" }); }) .catch(function (e) { // Initialization failed (invalid App ID, missing service worker, etc.) console.log("[OneSignal] init failed", e); }); });</script>
Setting the External ID is optional but recommended because it allows you to identify users across devices and syncs with your backend.Push ONESIGNAL_EXTERNAL_ID into the dataLayerThis example shows how you might push a user ID into the dataLayer so GTM can read it via the ONESIGNAL_EXTERNAL_ID variable (created in step 2).
HTML
Copy
Ask AI
<script> window.dataLayer = window.dataLayer || []; // Get your user ID from your database or auth system. // Ensure this is a string value. var userId = "your_user_id_here"; dataLayer.push({ ONESIGNAL_EXTERNAL_ID: String(userId), });</script>
Create a GTM Tag to set the External ID
Tag configuration:
Tag name: OneSignal – Set External ID
Tag type: Custom HTML
Tag firing options: Once per page
Trigger:
Create a custom event trigger for OneSignalInitialized (set in the above OneSignal - Init tag) and
Optionally if you know the user ID is available on the page load.
The required method to set the External ID is OneSignal.login(externalId) where externalId is a string.If {{ONESIGNAL_EXTERNAL_ID}} is empty (or GTM substitutes “undefined” / “null”), the login call will be skipped and the External ID will not be set. This is a common GTM timing issue.
Copy
Ask AI
<script> // OneSignalDeferred ensures this runs after the OneSignal SDK is ready window.OneSignalDeferred = window.OneSignalDeferred || []; window.OneSignalDeferred.push(function (OneSignal) { /* Read the External ID from Google Tag Manager. This should be a GTM variable (Data Layer Variable or Custom JS Variable). */ var externalId = "{{ONESIGNAL_EXTERNAL_ID}}"; console.log("[OneSignal] raw external ID from GTM:", externalId); /* Basic validation: - GTM may substitute undefined/null as strings - OneSignal.login requires a string */ if (!externalId || externalId === "undefined" || externalId === "null") { console.log("[OneSignal] External ID missing, skipping login"); return; } // Ensure the External ID is a clean string externalId = String(externalId).trim(); console.log("[OneSignal] Calling OneSignal.login with External ID:", externalId); /* Log the user into OneSignal using the External ID. This links the current browser/device to this user. */ OneSignal.login(externalId) .then(function () { console.log("[OneSignal] External ID set successfully:", externalId); }) .catch(function (e) { console.log("[OneSignal] Failed to set External ID", e); }); });</script>
If your site uses Consent Mode / a CMP, decide whether OneSignal should load:
Only after consent (common for EU/UK), or
Immediately (common where “functional” storage is allowed by default).
GTM supports a Consent Initialization trigger and tag-level consent controls to manage tag behavior based on user consent. However, OneSignal also provides privacy consent methods to control when the SDK loads.