npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-whatsapp-embedded-signup

v1.4.0

Published

React hook, button, and manual OAuth helpers for Meta WhatsApp Embedded Signup (SDK popup, URL builder, popup flow)

Readme

react-whatsapp-embedded-signup

React hook, button, and helpers for Meta’s WhatsApp Embedded Signup — onboard a WhatsApp Business Account (WABA) without wiring the Facebook SDK by hand.

Supports:

  • JS SDK popupFB.login with config_id
  • OAuth URL — build facebook.com/.../dialog/oauth links yourself
  • OAuth popup — open that URL in a popup, capture the auth code, close popup, call onSuccess

Install

npm install react-whatsapp-embedded-signup
yarn add react-whatsapp-embedded-signup
pnpm add react-whatsapp-embedded-signup

Peer dependency: react / react-dom >= 16.8.

Dummy credentials (examples below)

Use these placeholder values in docs and local testing — replace with your real Meta app values.

| Key | Dummy value | |-----|-------------| | App ID | 1191095509611003 | | Config ID | 3847291056382741 | | App Secret | a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 (server only) | | API version | v25.0 | | OAuth state | signup-7f3a9c |

Example onSuccess payload:

{
  "code": "AQBx7k2mN9pQwR4sT6uV8wX0yZ1aB3cD5eF7gH9iJ0kL2mN4oP6qR8sT",
  "redirectUri": "https://app.acme-corp.com/settings/whatsapp",
  "sessionInfo": {
    "waba_id": "123456789012345",
    "phone_number_id": "987654321098765",
    "business_id": "112233445566778"
  }
}

What it does

  • Loads the Facebook JavaScript SDK once and initializes it with your App ID
  • Opens embedded signup via FB.login(..., { config_id }) or an OAuth URL
  • Listens for WA_EMBEDDED_SIGNUP postMessage events (waba_id, phone_number_id, business_id)
  • Returns a short-lived authorization code plus the matching redirectUri for your backend token exchange
  • Checks FB.getLoginStatus when the SDK is ready
  • OAuth popup: closes on redirect, captures code, always passes it to onSuccess

Meta prerequisites

  1. Create a Meta App and add the WhatsApp product.
  2. Under WhatsApp → Embedded Signup, create a configuration → copy config_id.
  3. Add your URLs under Valid OAuth Redirect URIs, e.g.:
    • http://localhost:5173/
    • https://app.acme-corp.com/settings/whatsapp
  4. Keep App ID in the client; keep App Secret on the server only.

Usage — button

import { WhatsAppEmbeddedSignupButton } from 'react-whatsapp-embedded-signup';

function ConnectWhatsApp() {
  return (
    <WhatsAppEmbeddedSignupButton
      appId="1191095509611003"
      configId="3847291056382741"
      onSuccess={async ({ code, sessionInfo, redirectUri }) => {
        // code is always a non-empty string
        await fetch('/api/whatsapp/connect', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            code,
            redirectUri,
            wabaId: sessionInfo?.waba_id,           // "123456789012345"
            phoneNumberId: sessionInfo?.phone_number_id, // "987654321098765"
          }),
        });
      }}
      onError={(err) => console.error(err.code, err.message, err.details)}
    >
      Connect WhatsApp Business
    </WhatsAppEmbeddedSignupButton>
  );
}

Usage — hook (JS SDK popup)

import { useWhatsAppEmbeddedSignup } from 'react-whatsapp-embedded-signup';

function SdkSignup() {
  const { launchSignup, isSdkReady, isLaunching } = useWhatsAppEmbeddedSignup({
    appId: '1191095509611003',
    configId: '3847291056382741',
    apiVersion: 'v25.0',
    onSuccess: ({ code, sessionInfo, redirectUri }) => {
      console.log(code);        // "AQBx7k2mN9pQwR4sT6uV8wX0yZ1aB3cD5eF7gH9iJ0kL2mN4oP6qR8sT"
      console.log(redirectUri); // "https://staticxx.facebook.com/x/connect/xd_arbiter/?version=46"
      console.log(sessionInfo?.waba_id); // "123456789012345"
    },
    onError: (err) => alert(err.message),
    onEvent: (raw) => console.debug('signup step', raw),
  });

  return (
    <button type="button" onClick={launchSignup} disabled={!isSdkReady || isLaunching}>
      Connect WhatsApp
    </button>
  );
}

Usage — hook (OAuth popup)

Opens OAuth in a popup, closes on redirect, captures code, calls onSuccess:

import { useWhatsAppEmbeddedSignup } from 'react-whatsapp-embedded-signup';

function OAuthPopupSignup() {
  const { launchOAuth, isLaunching } = useWhatsAppEmbeddedSignup({
    appId: '1191095509611003',
    configId: '3847291056382741',
    nav: 'popup',
    state: 'signup-7f3a9c',
    // redirectUri defaults to current page URL — works on any host/path
    onSuccess: ({ code, redirectUri, sessionInfo }) => {
      fetch('/api/whatsapp/connect', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ code, redirectUri, ...sessionInfo }),
      });
    },
    onError: (err) => alert(err.message),
  });

  return (
    <button type="button" onClick={launchOAuth} disabled={isLaunching}>
      Connect via OAuth popup
    </button>
  );
}

Usage — launchOAuthPopup (one call)

import { launchOAuthPopup } from 'react-whatsapp-embedded-signup';

const stop = launchOAuthPopup({
  appId: '1191095509611003',
  configId: '3847291056382741',
  state: 'signup-7f3a9c',
  onDone: ({ callback, redirectUri }) => {
    // callback.code → "AQBx7k2mN9pQwR4sT6uV8wX0yZ1aB3cD5eF7gH9iJ0kL2mN4oP6qR8sT"
    if (callback.code) {
      exchangeOnServer(callback.code, redirectUri);
    }
  },
  onError: (err) => console.error(err.message),
});

// stop() to cancel watching

Usage — build OAuth URL

import { buildOAuthUrl, resolveRedirectUri } from 'react-whatsapp-embedded-signup';

const url = buildOAuthUrl({
  appId: '1191095509611003',
  configId: '3847291056382741',
  redirectUri: resolveRedirectUri(), // e.g. "https://app.acme-corp.com/settings/whatsapp"
  state: 'signup-7f3a9c',
});

// https://www.facebook.com/v25.0/dialog/oauth?client_id=1191095509611003&redirect_uri=...

Usage — OAuth popup (manual)

import { openOAuthPopup, watchOAuthPopup } from 'react-whatsapp-embedded-signup';

const { popup, redirectUri } = openOAuthPopup({
  appId: '1191095509611003',
  configId: '3847291056382741',
  state: 'signup-7f3a9c',
});

watchOAuthPopup({
  popup,
  redirectUri,
  onDone: ({ callback, redirectUri: uri }) => {
    if (callback.code) exchangeOnServer(callback.code, uri);
  },
  onError: (err) => console.error(err.message),
});

Usage — OAuth full-page redirect

import { startOAuth, parseOAuthCallback } from 'react-whatsapp-embedded-signup';

startOAuth({
  appId: '1191095509611003',
  configId: '3847291056382741',
  redirectUri: 'https://app.acme-corp.com/settings/whatsapp',
  state: 'signup-7f3a9c',
  nav: 'replace',
});

// On return:
const result = parseOAuthCallback();
if (result?.code) {
  exchangeOnServer(result.code, 'https://app.acme-corp.com/settings/whatsapp');
}

Backend: exchange the code

GET https://graph.facebook.com/v25.0/oauth/access_token
  ?client_id=1191095509611003
  &client_secret=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
  &redirect_uri=https://app.acme-corp.com/settings/whatsapp
  &code=AQBx7k2mN9pQwR4sT6uV8wX0yZ1aB3cD5eF7gH9iJ0kL2mN4oP6qR8sT

Example response:

{
  "access_token": "EAABsbCS1iHgBO7ZC8ZAZB4vZC...",
  "token_type": "bearer",
  "expires_in": 5184000
}

| Flow | redirect_uri to send | |------|------------------------| | SDK popup | From onSuccess.redirectUri — Facebook xd_arbiter URL by default | | OAuth popup / redirect | From onSuccess.redirectUri — your page URL, e.g. https://app.acme-corp.com/settings/whatsapp |

Server helper (auto-detect current page from request headers):

import { redirectFromReq } from 'react-whatsapp-embedded-signup';

// POST /api/exchange-token  { "code": "AQBx7k2m..." }
const redirectUri = redirectFromReq(req.headers);

Never expose your App Secret in the browser.

API

useWhatsAppEmbeddedSignup(options)

| Option | Type | Description | |--------|------|-------------| | appId | string | Meta App ID, e.g. 1191095509611003 | | configId | string | Embedded Signup config ID, e.g. 3847291056382741 | | apiVersion | string | Default v25.0 (DEFAULT_API_VERSION) | | extras | object | Merged into FB.login extras.setup | | exchangeUri | string | SDK token-exchange URI; defaults to Facebook xd_arbiter static URL | | redirectUri | string | OAuth dialog redirect URI; defaults to current page URL | | state | string | OAuth state param, e.g. signup-7f3a9c | | nav | 'popup' \| 'replace' \| 'assign' | How launchOAuth opens the dialog; default popup | | onSuccess | (result) => void | { code, sessionInfo, redirectUri }code is always present | | onError | (error) => void | Includes code / details when set | | onEvent | (event) => void | Every raw WA_EMBEDDED_SIGNUP message |

Returns:

| Property | Type | Description | |----------|------|-------------| | launchSignup | () => void | JS SDK popup (FB.login) | | launchOAuth | () => void | OAuth popup (default) or full-page redirect | | isSdkReady | boolean | Facebook SDK loaded | | isLaunching | boolean | Signup in progress | | loginStatus | string \| null | FB.getLoginStatus status | | authResponse | object \| null | Latest auth response | | checkLoginStatus | () => void | Re-run FB.getLoginStatus | | isCheckingLoginStatus | boolean | Status check in flight |

WhatsAppEmbeddedSignupButton

Same options as the hook, plus standard button props (className, style, disabled, children, …).

OAuth helpers

| Export | Description | |--------|-------------| | buildOAuthUrl | Build facebook.com/{version}/dialog/oauth?... | | launchOAuthPopup | Open popup + watch + close + return code | | openOAuthPopup | window.open the OAuth URL | | watchOAuthPopup | Watch popup until code or close | | startOAuth | Open OAuth flow (popup, replace, or assign) | | parseOAuthCallback | Parse ?code= / #code= / ?error= from URL | | relayPopupToOpener | Callback page: post code to opener (cross-origin safe) | | resolveCallbackRedirectUri | Callback page: rebuild exact redirect_uri from current URL | | isTrustedOAuthMessageOrigin | Validate OAuth postMessage origin | | oauthRedirectOrigin | Origin of a redirect URI | | resolveRedirectUri | Current page URL (origin + pathname) | | normalizeRedirectUri | Normalize any URL to OAuth redirect_uri shape | | resolveExchangeUri | SDK token-exchange redirect URI | | redirectFromReq | Resolve redirect URI from HTTP request headers | | FB_STATIC_URI | Facebook JS SDK xd_arbiter URL | | DEFAULT_API_VERSION | v25.0 |

Notes

  • Auth codes expire in ~30 seconds — exchange on your server immediately.
  • onSuccess always receives a non-empty code; otherwise onError fires with NO_AUTH_CODE.
  • onError also fires for user cancel (err.code === 'CANCELLED').
  • OAuth redirect_uri defaults to the current page — works on any URL; register each URL in Meta.
  • OAuth popup requires popups allowed for your domain.
  • Cross-origin callback — opener and callback can be different domains. The callback relays the code via postMessage to the opener. Register the exact redirect URI in Meta.

Cross-origin callback (e.g. API domain)

Opener (admin app):

const { launchOAuth } = useWhatsAppEmbeddedSignup({
  appId,
  configId,
  redirectUri: 'https://test-admin-api.wayvida.com/token/callback',
  deferRelay: true, // callback handles API first, then relays manually
  onSuccess: ({ code, redirectUri }) => {
    console.log('Got code in opener', code, redirectUri);
  },
});

Callback page (https://test-admin-api.wayvida.com/token/callback):

import { parseOAuthCallback, relayPopupToOpener, resolveCallbackRedirectUri } from 'react-whatsapp-embedded-signup';

const result = parseOAuthCallback();
const redirectUri = resolveCallbackRedirectUri();

async function finish() {
  try {
    await fetch('/api/exchange-token', {
      method: 'POST',
      body: JSON.stringify({ code: result?.code, redirectUri }),
    });
    // show success UI
  } catch {
    // show error UI
  }

  relayPopupToOpener({ close: false }); // send code to opener admin app
  setTimeout(() => window.close(), 10_000);
}

if (result?.code) void finish();

Without deferRelay, the callback auto-relays and closes immediately on load.

License

MIT