Appearance
OneSignal – Integration Guide
Scope: Web push notifications in the React app via OneSignal. This document covers environment variables, initialization, usage patterns, testing, and troubleshooting.
Quick Links by Role
- 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.localwithVITE_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.examplewith 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 devonhttp://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=trueto 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_IDis set and accessible in the build. - Ensure HTTPS (or localhost) and browser supports notifications.
- Check
- SDK errors in console:
- Version mismatch; verify you use the Web SDK (not Cordova) or adjust typings.
- Re‑check initialization ordering (call
initOneSignal()once, afterwindowexists).
- Users not receiving notifications:
- Confirm permission granted and device registered in OneSignal dashboard.
- Ensure
external_idis 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_IDis set in.env.localand not committed. - SDK v16 loaded in
index.html; do not duplicate initialization in React. - Use
loginOneSignal(userId)after successful auth; calllogoutOneSignal()on sign out. - Use
promptPushPermission()viaPushPromptand 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:
- Go to
/admin/templates→ Timeline tab. - Find the event (e.g.,
offer_sent). - Toggle the desired role columns under "Buyer Push" / "Seller Push" / "Admin Push".
- 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_idwho 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).