link,[object Object]
Skip to content

OneSignal – Integration Guide

Scope: Web push notifications in the React app via OneSignal. This document covers environment variables, initialization, usage patterns, testing, and troubleshooting.


  • Product: see Role Guidance → Product
  • Developer: see Env Vars, Initialization, Identifying Users, Sending, Troubleshooting
  • Admin/Operations: see Monitoring, QA & Testing, Security & Privacy
  • Support: see Troubleshooting and Acceptance Criteria

1. Overview

  • Purpose: transactional and marketing notifications via web push.
  • Stack context:
    • Frontend: Vite + React + TypeScript
    • Integration entry: src/lib/onesignal.ts
    • UI opt‑in component: src/components/notifications/PushPrompt.tsx
  • Environments: dev/stage/prod using .env.local with VITE_ variables.

2. Environment Variables

Define in .env.local (do not commit secrets):

bash
# OneSignal (Web Push)
VITE_ONESIGNAL_APP_ID="<your-onesignal-app-id>"   # Required
VITE_ONESIGNAL_SAFARI_WEB_ID="<optional-safari-web-id>"  # Optional (Safari push)
VITE_ONESIGNAL_ALLOW_LOCALHOST="true"            # Optional, for local dev prompts
  • Keep placeholders in .env.example with comments.
  • Frontend vars must be prefixed with VITE_.

3. Initialization (Code Reference)

Initialization happens in index.html with the v16 Web SDK and VITE_ONESIGNAL_APP_ID from env.

95:132:index.html
<!-- OneSignal Web SDK v16 (load before app) -->
<script type="module">
  // @ts-ignore
  window.__VITE_ONESIGNAL_APP_ID__ = import.meta.env?.VITE_ONESIGNAL_APP_ID;
  </script>
<script src="https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js"></script>
<script>
  window.OneSignalDeferred = window.OneSignalDeferred || [];
  OneSignalDeferred.push(function(OneSignal) {
    try {
      var origin = window.location.origin;
      var isLocalhost = origin.includes('localhost') || origin.includes('127.0.0.1');
      var appId = (window.__VITE_ONESIGNAL_APP_ID__ || window.VITE_ONESIGNAL_APP_ID || (window.env && window.env.VITE_ONESIGNAL_APP_ID) || "");
      if (!appId) return;
      var initOptions = { appId: appId };
      if (isLocalhost) { initOptions.allowLocalhostAsSecureOrigin = true; }
      OneSignal.init(initOptions);
      window.__ONESIGNAL_INIT_ERROR__ = false;
    } catch (e) {
      window.__ONESIGNAL_INIT_ERROR__ = true;
      console.warn('OneSignal init error:', e);
    }
  });
</script>

Helper APIs exposed in src/lib/onesignal.ts:

1:74:src/lib/onesignal.ts
export function getOneSignal(): any | null { /* ... */ }
export function loginOneSignal(userId: string) { /* deferred login */ }
export function logoutOneSignal() { /* logout if available */ }
export async function promptPushPermission() { /* request permission */ }

4. Prompting Users (Opt‑In)

The repo provides a lightweight prompt component.

1:120:src/components/notifications/PushPrompt.tsx
import { useEffect, useState } from 'react';

export default function PushPrompt() {
  const [canPrompt, setCanPrompt] = useState(false);

  useEffect(() => {
    if (typeof window === 'undefined') return;
    // Basic heuristics to show prompt only when supported
    const isSupported = 'Notification' in window;
    setCanPrompt(isSupported);
  }, []);

  async function requestPermission() {
    try {
      // @ts-ignore
      const result = await window?.OneSignal?.Notifications?.requestPermission();
      console.log('Push permission result', result);
    } catch (e) {
      console.error('Push permission error', e);
    }
  }

  if (!canPrompt) return null;

  return (
    <button onClick={requestPermission}>
      Enable push notifications
    </button>
  );
}

Guidelines:

  • Gate prompt by route and session state; avoid prompting on every visit.
  • Consider a dismiss state in localStorage to reduce friction.

5. Identifying Users

To target notifications, set external user identifiers when available:

ts
// After auth login or whenever userId is known
// @ts-ignore
window.OneSignalDeferred = window.OneSignalDeferred || [];
window.OneSignalDeferred.push(function(OneSignal: any) {
  OneSignal.login('<user-id>'); // maps device to your userId
});

Avoid sending PII; prefer stable, opaque user IDs.


6. Sending Notifications

  • Primary sending should be done from OneSignal Dashboard or backend automations.
  • For app‑generated events, emit to OneSignal via server (recommended) to avoid exposing keys in frontend.
  • If using Supabase Edge Functions, keep OneSignal REST keys in environment (never in frontend) and call OneSignal REST API.

Basic REST example (server‑side only):

http
POST https://api.onesignal.com/notifications
Authorization: Basic <REST_API_KEY>
Content-Type: application/json

{
  "app_id": "<ONESIGNAL_APP_ID>",
  "include_aliases": { "external_id": ["<user-id>"] },
  "headings": { "en": "Update available" },
  "contents": { "en": "Your deal has a new message" }
}

7. Local Development

  • Serve via pnpm dev on http://localhost:8080.
  • For Chrome, localhost is treated as secure; still ensure proper service worker path if using advanced features.
  • Optionally set VITE_ONESIGNAL_ALLOW_LOCALHOST=true to bypass certain environment checks in your integration.

8. QA & Testing Checklist

  • Verify init runs without errors (DevTools console clean).
  • Permission flow: prompt → grant/deny → state reflected in UI.
  • User association: login flows set external user ID.
  • Send a test notification from OneSignal dashboard to the test user.
  • Responsive behavior on mobile and desktop.
  • Ensure prompts are rate‑limited (no nagging).

9. Troubleshooting

  • No prompt appears:
    • Check VITE_ONESIGNAL_APP_ID is set and accessible in the build.
    • Ensure HTTPS (or localhost) and browser supports notifications.
  • SDK errors in console:
    • Version mismatch; verify you use the Web SDK (not Cordova) or adjust typings.
    • Re‑check initialization ordering (call initOneSignal() once, after window exists).
  • Users not receiving notifications:
    • Confirm permission granted and device registered in OneSignal dashboard.
    • Ensure external_id is set for targeted sends.

10. Security & Privacy

  • Never expose REST API keys in frontend; keep them in server/Edge functions.
  • Do not send PII to OneSignal; use opaque IDs.
  • Document consent and provide clear opt‑out in account settings.

11. Role‑Based Guidance

Product

  • Goals: timely, permission‑respecting alerts for listings, deals, and messages.
  • UX: prompt only after meaningful action (e.g., saved search, watchlist add); avoid disruptive modals.
  • Copy: concise and actionable (max ~45 chars title, ~120 body), include clear value.
  • Segmentation: use external_id to target users by role (buyer/seller) when sending via dashboard or backend.

Developer

  • Ensure VITE_ONESIGNAL_APP_ID is set in .env.local and not committed.
  • SDK v16 loaded in index.html; do not duplicate initialization in React.
  • Use loginOneSignal(userId) after successful auth; call logoutOneSignal() on sign out.
  • Use promptPushPermission() via PushPrompt and respect local cooldown.
  • Never expose OneSignal REST API key in frontend; server/Edge only.

Admin / Operations

  • Monitoring: verify device registrations and deliveries in OneSignal Dashboard.
  • Rate limiting/retries are handled by OneSignal; avoid excessive test sends.
  • Compliance: confirm consent and regional policies (GDPR, ePrivacy) are documented.
  • Incidents: when deliverability drops, check domain/HTTPS validity, permission churn, and browser updates.

Support

  • Common issues: no prompt (permissions blocked, unsupported browser), not receiving (not subscribed, wrong segment), browser‑level blocks.
  • Resolution steps: follow Troubleshooting; escalate to Dev if SDK errors persist.

12. Acceptance Criteria

  • App initializes OneSignal without runtime errors.
  • Users can opt in to push and receive a test notification.
  • External user IDs are set post‑login for targeting.
  • Docs reflect env vars and integration points accurately.

13. Admin Templates (/admin/templates) ↔ OneSignal

Location: Admin → Templates → Timeline / In‑App Notifications

What it controls:

  • Timeline tab: per‑event, per‑role channel toggles (Email, App, Push). The columns "Buyer Push" / "Seller Push" / "Admin Push" enable OneSignal deliveries for that event+role.
  • In‑App Notifications tab: template texts used for app/push notifications. Titles/bodies here are the source strings for OneSignal payloads.

How to enable push for an event:

  1. Go to /admin/templates → Timeline tab.
  2. Find the event (e.g., offer_sent).
  3. Toggle the desired role columns under "Buyer Push" / "Seller Push" / "Admin Push".
  4. Click "Edit" to adjust titles/bodies in the In‑App Notifications tab if needed.

Delivery path:

  • App UI triggers → DB/Edge logic builds notification → If Push toggle ON, a OneSignal send is issued targeting users with matching external_id who granted permission.

Notes:

  • Ensure OneSignal is initialized and user logged in via loginOneSignal(userId).
  • If a user didn’t grant permissions, they won’t receive push even if the toggle is ON (they may still see in‑app notifications).