Document Stack
Document Stack
Docs

React Integration

Integrate Document Stack PDF generation into React applications with hooks and components.

Architecture

React runs in the browser, so PDF generation must happen through your backend. The typical flow:

  1. React component calls your backend API
  2. Backend uses the Document Stack SDK to generate a PDF
  3. Backend returns the PDF URL to the React app
  4. React displays a download link or opens the PDF

Never Expose API Keys

Do not use the Document Stack SDK directly in React client code. Always generate PDFs from your backend.

Custom Hook

Create a reusable hook for PDF generation:

hooks/useGeneratePdf.ts
import { useState } from "react";

interface GeneratePdfOptions {
    templateId: string;
    data: Record<string, unknown>;
}

export function useGeneratePdf() {
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState<string | null>(null);
    const [pdfUrl, setPdfUrl] = useState<string | null>(null);

    async function generate(options: GeneratePdfOptions) {
        setLoading(true);
        setError(null);

        try {
            const response = await fetch("/api/generate", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify(options),
            });

            const result = await response.json();

            if (!result.success) {
                throw new Error(result.error);
            }

            setPdfUrl(result.data.url);
            return result.data.url;
        } catch (err) {
            const message = err instanceof Error ? err.message : "Generation failed";
            setError(message);
            return null;
        } finally {
            setLoading(false);
        }
    }

    return { generate, loading, error, pdfUrl };
}

Using the Hook

InvoiceGenerator.tsx
import { useGeneratePdf } from "./hooks/useGeneratePdf";

export function InvoiceGenerator() {
    const { generate, loading, error, pdfUrl } = useGeneratePdf();

    const handleClick = () => {
        generate({
            templateId: "tmpl_invoice",
            data: {
                customerName: "Acme Corp",
                amount: 499.99,
                date: new Date().toISOString(),
            },
        });
    };

    return (
        <div>
            <button onClick={handleClick} disabled={loading}>
                {loading ? "Generating..." : "Generate Invoice"}
            </button>

            {error && <p style={{ color: "red" }}>{error}</p>}

            {pdfUrl && (
                <a href={pdfUrl} target="_blank" rel="noopener noreferrer">
                    📄 Download Invoice
                </a>
            )}
        </div>
    );
}

With React Query

For more robust state management, use React Query (TanStack Query):

Using React Query
import { useMutation } from "@tanstack/react-query";

function useGeneratePdfMutation() {
    return useMutation({
        mutationFn: async (options: {
            templateId: string;
            data: Record<string, unknown>;
        }) => {
            const res = await fetch("/api/generate", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify(options),
            });
            const result = await res.json();
            if (!result.success) throw new Error(result.error);
            return result.data;
        },
    });
}

// In your component
function InvoiceButton() {
    const mutation = useGeneratePdfMutation();

    return (
        <button
            onClick={() =>
                mutation.mutate({
                    templateId: "tmpl_invoice",
                    data: { customerName: "Alice" },
                })
            }
            disabled={mutation.isPending}
        >
            {mutation.isPending ? "Generating..." : "Generate"}
        </button>
    );
}

In-Browser PDF Preview

Display the generated PDF inline using an iframe:

PDF Viewer Component
function PdfViewer({ url }: { url: string }) {
    return (
        <iframe
            src={url}
            width="100%"
            height="600px"
            style={{ border: "1px solid #e5e7eb", borderRadius: "8px" }}
            title="Generated PDF"
        />
    );
}

Next Steps