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

whatsapp-business-api-nextjs

v0.1.1

Published

Full-stack WhatsApp Business API toolkit for Next.js - server components, webhook handling, and React UI

Readme

WhatsApp Business Next.js Library

A full-stack toolkit for integrating WhatsApp Business API into Next.js applications.

Features

  • Multi-Phone Support: Manage multiple numbers under one Meta account with phone selector UI
  • Database Agnostic: Implement the IWhatsAppStorage interface with your own Prisma/Drizzle/MongoDB adapter
  • Ready UI Components: Pre-built Chat Windows, Conversation Lists, Template Pickers, Message Composers, and Phone Selectors
  • Secure Webhooks: HMAC-SHA256 signature verification for incoming Meta webhooks
  • Observer Pattern: Subscribe to real-time events (message:received, status:updated, error)
  • Template Support: Send and reconstruct template messages with variable substitution and clickable buttons

Installation

npm install whatsapp-business-api-nextjs

Quick Start

1. Implement Storage Interface

Create your own storage adapter implementing IWhatsAppStorage:

import { IWhatsAppStorage, WhatsAppMessageRecord, WhatsAppConversation } from 'whatsapp-business-api-nextjs';

export class MyDatabaseStorage implements IWhatsAppStorage {
  async saveMessage(record: WhatsAppMessageRecord): Promise<void> {
    // Save to your database
  }

  async updateMessageStatus(waMessageId: string, status: string, timestamp: Date): Promise<void> {
    // Update message status in your database
  }

  async getHistory(customerNumber: string, phoneNumberId: string): Promise<WhatsAppMessageRecord[]> {
    // Retrieve messages for a customer + phone combination
  }

  async getConversations(phoneNumberId: string): Promise<WhatsAppConversation[]> {
    // Get all conversations for a phone number
  }
}

2. Initialize Client (Server-Side)

import { WhatsAppClient } from 'whatsapp-business-api-nextjs';
import { myStorage } from './my-storage';

export const waClient = new WhatsAppClient({
  accessToken: process.env.WA_ACCESS_TOKEN!,
  accountId: process.env.WA_ACCOUNT_ID!,
  proxyUrl: '/api/whatsapp/proxy', // For frontend-to-backend communication
}, myStorage);

3. Create API Proxy Handler

In pages/api/whatsapp/proxy.ts (Pages Router) or app/api/whatsapp/proxy/route.ts (App Router):

import { createWhatsAppHandler } from 'whatsapp-business-api-nextjs';
import { waClient, myStorage } from '@/lib/whatsapp';

// Pages Router
export default createWhatsAppHandler(waClient, myStorage);

// App Router
const handler = createWhatsAppHandler(waClient, myStorage);
export { handler as POST };

4. Set Up Provider (Client-Side)

import { WhatsAppProvider } from 'whatsapp-business-api-nextjs';
import 'whatsapp-business-api-nextjs/styles';

// Create a client-side instance (no storage needed)
const clientSideWaClient = new WhatsAppClient({
  accessToken: '', // Not used on client
  accountId: process.env.NEXT_PUBLIC_WA_ACCOUNT_ID!,
  proxyUrl: '/api/whatsapp/proxy',
});

export default function App({ Component, pageProps }) {
  return (
    <WhatsAppProvider client={clientSideWaClient}>
      <Component {...pageProps} />
    </WhatsAppProvider>
  );
}

5. Use Components

import { 
  PhoneSelector,
  ConversationList,
  MessageHistory,
  MessageComposer,
  TemplatePicker,
  TemplateForm,
  useWhatsApp 
} from 'whatsapp-business-api-nextjs';

export default function WhatsAppDashboard() {
  const { activePhone } = useWhatsApp();
  const [selectedCustomer, setSelectedCustomer] = useState<string | null>(null);

  return (
    <div className="dashboard">
      {/* Sidebar */}
      <aside>
        <PhoneSelector label="Send from:" />
        <ConversationList 
          phoneNumberId={activePhone?.phoneNumberId}
          onSelectConversation={(conv) => setSelectedCustomer(conv.customerNumber)}
        />
      </aside>

      {/* Chat Area */}
      <main>
        {selectedCustomer && activePhone && (
          <>
            <MessageHistory 
              customerNumber={selectedCustomer}
              phoneNumberId={activePhone.phoneNumberId}
            />
            <MessageComposer
              customerNumber={selectedCustomer}
              phoneNumberId={activePhone.phoneNumberId}
              onMessageSent={() => console.log('Sent!')}
            />
          </>
        )}
      </main>
    </div>
  );
}

Webhook Setup

Secure Webhook Handler

import { WhatsAppWebhookHandler } from 'whatsapp-business-api-nextjs';
import { myStorage } from './my-storage';

const webhookHandler = new WhatsAppWebhookHandler({
  verifyToken: process.env.WA_VERIFY_TOKEN!,
  appSecret: process.env.WA_APP_SECRET!, // Required for signature verification
}, myStorage);

// Subscribe to events (Observer Pattern)
webhookHandler.on('message:received', (event) => {
  console.log('New message from:', event.message.customerNumber);
  console.log('Content:', event.message.content);
  // Trigger notifications, update UI, etc.
});

webhookHandler.on('status:updated', (event) => {
  console.log('Message status:', event.status.status); // sent, delivered, read, failed
});

webhookHandler.on('error', (event) => {
  console.error('Webhook error:', event.error);
});

Pages Router (pages/api/whatsapp/webhook.ts)

import type { NextApiRequest, NextApiResponse } from 'next';
import { webhookHandler } from '@/lib/whatsapp';

export const config = {
  api: { bodyParser: false }, // Required for signature verification
};

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'GET') {
    // Webhook verification
    const result = webhookHandler.verify({
      'hub.mode': req.query['hub.mode'] as string,
      'hub.verify_token': req.query['hub.verify_token'] as string,
      'hub.challenge': req.query['hub.challenge'] as string,
    });
    
    if (result.startsWith('Error')) {
      return res.status(403).send(result);
    }
    return res.status(200).send(result);
  }

  if (req.method === 'POST') {
    // Get raw body for signature verification
    const chunks: Buffer[] = [];
    for await (const chunk of req) {
      chunks.push(chunk);
    }
    const rawBody = Buffer.concat(chunks).toString('utf8');
    const signature = req.headers['x-hub-signature-256'] as string;

    // Verify signature
    if (!webhookHandler.verifySignature(rawBody, signature)) {
      return res.status(401).json({ error: 'Invalid signature' });
    }

    // Process webhook
    const payload = JSON.parse(rawBody);
    const result = await webhookHandler.handle(payload);
    return res.status(200).json({ result });
  }

  res.status(405).end();
}

App Router (app/api/whatsapp/webhook/route.ts)

import { webhookHandler } from '@/lib/whatsapp';

export async function GET(request: Request) {
  const params = new URL(request.url).searchParams;
  const result = webhookHandler.verify({
    'hub.mode': params.get('hub.mode') || '',
    'hub.verify_token': params.get('hub.verify_token') || '',
    'hub.challenge': params.get('hub.challenge') || '',
  });
  
  if (result.startsWith('Error')) {
    return new Response(result, { status: 403 });
  }
  return new Response(result, { status: 200 });
}

export async function POST(request: Request) {
  const rawBody = await request.text();
  const signature = request.headers.get('x-hub-signature-256') || '';

  if (!webhookHandler.verifySignature(rawBody, signature)) {
    return Response.json({ error: 'Invalid signature' }, { status: 401 });
  }

  const payload = JSON.parse(rawBody);
  const result = await webhookHandler.handle(payload);
  return Response.json({ result });
}

Sending Templates

import { useWhatsApp } from 'whatsapp-business-api-nextjs';

function SendTemplateButton({ template, customerNumber }) {
  const { client, activePhone } = useWhatsApp();

  const handleSend = async () => {
    await client.sendTemplate({
      fromPhoneNumberId: activePhone.phoneNumberId,
      to: customerNumber,
      templateName: template.name,
      languageCode: template.language,
      components: [
        {
          type: 'body',
          parameters: [
            { type: 'text', text: 'John Doe' },
            { type: 'text', text: 'ORDER-12345' },
          ],
        },
      ],
      // Include template for message reconstruction in storage
      templateSnapshot: template,
    });
  };

  return <button onClick={handleSend}>Send Template</button>;
}

Components Reference

| Component | Description | |-----------|-------------| | WhatsAppProvider | Context provider, wraps your app | | PhoneSelector | Dropdown to select active WhatsApp Business phone | | ConversationList | List of conversations for a phone number | | MessageHistory | Displays message history with a customer | | MessageComposer | Input for sending text/image/document messages | | TemplatePicker | Grid of available message templates | | TemplateForm | Form to fill template variables before sending | | MessageBubble | Individual message display with buttons support |

Hooks

| Hook | Returns | |------|---------| | useWhatsApp() | { client, templates, phones, activePhone, setActivePhone, isLoading, refreshData } |

Types

import type {
  // Config
  WhatsAppConfig,
  WhatsAppPhoneConfig,
  
  // Messages
  WhatsAppMessageRecord,
  WhatsAppConversation,
  
  // Templates
  WhatsAppTemplate,
  SendTemplateOptions,
  
  // Webhooks
  WebhookHandlerConfig,
  WebhookEvent,
  MessageReceivedEvent,
  StatusUpdatedEvent,
  WebhookErrorEvent,
  
  // Storage
  IWhatsAppStorage,
} from 'whatsapp-business-api-nextjs';

Environment Variables

# Required
WA_ACCESS_TOKEN=your_meta_access_token
WA_ACCOUNT_ID=your_whatsapp_business_account_id
WA_VERIFY_TOKEN=your_webhook_verify_token
WA_APP_SECRET=your_meta_app_secret

# Optional (for client-side)
NEXT_PUBLIC_WA_ACCOUNT_ID=your_whatsapp_business_account_id

License

GPL-3.0 - See LICENSE for details.