May 13, 2026 · React Integration

Build a React Signing Component with Signbee API

No SDK. No iframe. No OAuth dance. Just a React component that sends documents for signing in under 50 lines — and tracks status via webhooks.

Michael Beckett
Michael Beckett

Founder, Signbee

TL;DR

You don't need an SDK, an iframe, or a 200-line OAuth flow to add e-signatures to React. Signbee's single-endpoint API lets you trigger a document send with one fetch() call. Your component handles the UI — a button, a status badge, a callback. The API handles everything else: PDF generation from markdown, email delivery, signing page, audit trail.

The problem with embedded signing iframes

If you've tried adding e-signatures to a React app with DocuSign, you know the drill. Install the docusign-esign SDK (2.1 MB). Set up an OAuth integration with JWT or Authorization Code Grant. Create an envelope. Add a recipient. Generate a recipient view URL. Embed that URL in an iframe. Handle the redirect back.

The iframe approach creates real problems in production:

IssueImpact
CSP violationsiframe blocked in strict CSP environments
Mobile renderingiframe doesn't resize properly on mobile
Cross-origin cookiesSafari blocks third-party cookies in iframes
Session managementSigning session expires, no graceful recovery
Bundle sizeDocuSign SDK adds 2.1 MB to your node_modules

The root problem is architectural. DocuSign was built as a GUI-first product. The API is a wrapper around the GUI. When you embed signing, you're embedding their entire application inside yours. That's why it's fragile.

The API-first alternative

With an API-first signing provider, the architecture is different. Your React component calls one endpoint. The API generates a PDF from your markdown content, sends it to the signer via email, and hosts the signing experience on its own page. Your component doesn't embed anything — it triggers a send and tracks status.

This means no iframes, no CSP issues, no cross-origin cookies, no SDK. Just fetch().

The complete React component

Here's a production-ready signing component. It handles the send, tracks status, and shows feedback — all in under 50 lines of actual logic:

React — SigningButton.tsx
"use client";
import { useState } from "react";

type SigningStatus = "idle" | "sending" | "sent" | "error";

export function SigningButton({
  markdown,
  recipientName,
  recipientEmail,
}: {
  markdown: string;
  recipientName: string;
  recipientEmail: string;
}) {
  const [status, setStatus] = useState<SigningStatus>("idle");
  const [docId, setDocId] = useState<string | null>(null);

  async function handleSend() {
    setStatus("sending");
    try {
      const res = await fetch("https://signb.ee/api/v1/send", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${process.env.NEXT_PUBLIC_SIGNBEE_KEY}`,
        },
        body: JSON.stringify({
          markdown,
          recipient_name: recipientName,
          recipient_email: recipientEmail,
        }),
      });

      if (!res.ok) throw new Error(await res.text());

      const data = await res.json();
      setDocId(data.id);
      setStatus("sent");
    } catch {
      setStatus("error");
    }
  }

  return (
    <div>
      <button
        onClick={handleSend}
        disabled={status === "sending" || status === "sent"}
        className="px-4 py-2 bg-amber-500 text-black rounded disabled:opacity-50"
      >
        {status === "idle" && "Send for Signing"}
        {status === "sending" && "Sending..."}
        {status === "sent" && "✓ Sent"}
        {status === "error" && "Retry"}
      </button>
      {docId && (
        <p className="text-sm text-gray-500 mt-2">
          Document ID: {docId}
        </p>
      )}
    </div>
  );
}

That's the entire component. No SDK import. No OAuth token exchange. No envelope abstraction. The fetch() call sends markdown content, and the API returns a document ID you can use to track status.

Compare this to DocuSign's embedded signing

Here's what the same workflow looks like with DocuSign's API. I'm not exaggerating — this is the minimum viable implementation:

StepSignbeeDocuSign
Install dependenciesNone (fetch)docusign-esign (2.1 MB)
AuthenticationAPI key in headerOAuth JWT + consent URL
Create documentSend markdown in bodyBase64-encode PDF, create envelope
Add signerName + email in bodyRecipientViewRequest object
Display signingEmail link (no embed)iframe with recipient view URL
Lines of code~20~200
Time to integrate30 minutes2-4 hours

Tracking signing status with webhooks

The component above handles the send. But you also need to know when the signer opens, signs, or declines the document. Signbee fires webhook events to your server for every status change.

Next.js API Route — /api/webhooks/signbee/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
  const payload = await req.json();

  // Verify the webhook signature (recommended)
  const signature = req.headers.get("x-signbee-signature");

  switch (payload.event) {
    case "document.viewed":
      // Signer opened the document
      await db.documents.update({
        where: { id: payload.document_id },
        data: { status: "viewed", viewedAt: new Date() },
      });
      break;

    case "document.signed":
      // Signer completed signing
      await db.documents.update({
        where: { id: payload.document_id },
        data: {
          status: "signed",
          signedAt: new Date(),
          pdfUrl: payload.pdf_url,
        },
      });
      break;

    case "document.declined":
      await db.documents.update({
        where: { id: payload.document_id },
        data: { status: "declined" },
      });
      break;
  }

  return NextResponse.json({ received: true });
}

Adding a real-time status badge

Once your webhook handler stores status updates, your React component can poll or subscribe to display a live badge. Here's a simple polling approach that checks every 5 seconds after sending:

React — useSigningStatus.ts
import { useEffect, useState } from "react";

export function useSigningStatus(documentId: string | null) {
  const [status, setStatus] = useState<string>("pending");

  useEffect(() => {
    if (!documentId) return;

    const interval = setInterval(async () => {
      const res = await fetch(`/api/documents/${documentId}/status`);
      const data = await res.json();
      setStatus(data.status);

      if (data.status === "signed" || data.status === "declined") {
        clearInterval(interval);
      }
    }, 5000);

    return () => clearInterval(interval);
  }, [documentId]);

  return status;
}

For production apps handling more than a few concurrent documents, swap polling for WebSockets or Server-Sent Events. The webhook handler pushes status changes to a channel, and your React component subscribes.

Using the component in a real app

Here's how you'd use the signing component in a contract management page. The markdown content can be generated dynamically — pull in client names, dates, amounts, whatever your contract needs:

React — ContractsPage.tsx
import { SigningButton } from "@/components/SigningButton";

export default function ContractsPage() {
  const contractMarkdown = `
# Service Agreement

**Client:** Acme Corp
**Date:** ${new Date().toLocaleDateString()}
**Amount:** $5,000/month

## Terms

1. Services will begin on the effective date
2. Payment is due within 30 days of invoice
3. Either party may terminate with 30 days notice

## Signatures

By signing below, both parties agree to these terms.
`;

  return (
    <div className="p-8">
      <h1>Send Contract</h1>
      <SigningButton
        markdown={contractMarkdown}
        recipientName="Jane Smith"
        recipientEmail="jane@acme.com"
      />
    </div>
  );
}

The markdown is converted to a clean PDF by Signbee's API. No LaTeX. No Puppeteer. No wkhtmltopdf. You write markdown, the API renders it. See the API documentation for all the formatting options supported — headers, tables, bold, lists, and custom branding.

Error handling in production

The component above is clean but missing production error handling. Here's what I add to every signing component I deploy:

Network failureRetry with exponential backoff
401 UnauthorizedCheck API key, show config error
429 Rate LimitedQueue and retry after Retry-After
422 Validation ErrorShow field-level error to user
500 Server ErrorRetry once, then show support link

For rate limit handling patterns, see our complete rate limits guide with retry logic code examples.

Why this approach scales

The iframe approach couples your frontend to the signing provider's UI. If they redesign their signing page, your embedded experience breaks. If they change CSP headers, your integration breaks. If they deprecate their embed SDK, you rewrite everything.

The API-first approach decouples completely. Your React component owns the UI. The API handles document generation, delivery, signing, and storage. If you ever switch providers, you change one fetch() URL. The component doesn't care.

I built Signbee specifically for this pattern. One endpoint. Markdown in, signed PDF out. The MCP server lets AI agents trigger the same flow without a browser.

Frequently Asked Questions

Can I embed e-signatures directly in a React app?

Yes, but not with an iframe. With Signbee, your React component triggers a send via fetch(). The signer receives the document by email and signs on a hosted page. Your component tracks status via webhooks — no embedding required.

Do I need the DocuSign SDK to add signing to React?

No. DocuSign's SDK adds 2.1 MB and requires OAuth, envelope creation, and recipient view generation. With Signbee, a single fetch() replaces the entire flow — no SDK, no OAuth dance.

How do I track signing status in React?

Signbee sends webhook events (viewed, signed, declined) to your server. Push updates to your React frontend via WebSockets, SSE, or simple polling. See our webhooks guide for the full implementation.

How much does it cost?

Signbee offers 5 free documents/month. Paid plans are $0.50/doc with no monthly minimum — enough to build and test your integration for free, then scale without enterprise contracts.

Add signing to your React app in 30 minutes — 5 free docs/month.

Last updated: May 13, 2026 · Michael Beckett is the founder of Signbee and B2bee Ltd.

Related resources