Integrate with Next.js
Generate PDF documents from your Next.js application using an API route for the server-side logic and a client component for the UI.
Prerequisites
- A Next.js 13+ project (App Router)
- A Rynko account with an API key
- A published template (note the template ID)
Step 1: Install the SDK
npm install @rynko/sdk
Add your API key to .env.local:
.env.local
RYNKO_API_KEY=fm_abc123xyz456...
warning
The RYNKO_API_KEY variable intentionally omits the NEXT_PUBLIC_ prefix so it stays on the server and is never exposed to the browser.
Step 2: Create an API Route
Create a server-side route that handles document generation. The SDK runs only on the server, keeping your API key secure.
app/api/generate-invoice/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Rynko, RynkoError } from '@rynko/sdk';
const rynko = new Rynko({
apiKey: process.env.RYNKO_API_KEY!,
});
export async function POST(request: NextRequest) {
const { customerName, invoiceNumber, amount } = await request.json();
try {
const job = await rynko.documents.generatePdf({
templateId: 'YOUR_TEMPLATE_ID',
variables: { customerName, invoiceNumber, amount },
});
const completed = await rynko.documents.waitForCompletion(job.jobId, {
timeout: 30000,
});
if (completed.status === 'failed') {
return NextResponse.json(
{ error: completed.errorMessage },
{ status: 500 },
);
}
return NextResponse.json({ downloadUrl: completed.downloadUrl });
} catch (error) {
if (error instanceof RynkoError) {
return NextResponse.json(
{ error: error.message },
{ status: error.statusCode },
);
}
return NextResponse.json(
{ error: 'Document generation failed' },
{ status: 500 },
);
}
}
tip
Replace YOUR_TEMPLATE_ID with the ID of your template from the Rynko dashboard.
Step 3: Build the Client Component
Create a form component that calls your API route:
app/invoices/invoice-form.tsx
'use client';
import { useState } from 'react';
export function InvoiceForm() {
const [customerName, setCustomerName] = useState('');
const [amount, setAmount] = useState('');
const [downloadUrl, setDownloadUrl] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setLoading(true);
setError(null);
setDownloadUrl(null);
try {
const res = await fetch('/api/generate-invoice', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
customerName,
invoiceNumber: `INV-${Date.now()}`,
amount: parseFloat(amount),
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Generation failed');
setDownloadUrl(data.downloadUrl);
} catch (err) {
setError(err instanceof Error ? err.message : 'Something went wrong');
} finally {
setLoading(false);
}
}
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="customerName">Customer Name</label>
<input
id="customerName"
value={customerName}
onChange={(e) => setCustomerName(e.target.value)}
required
/>
</div>
<div>
<label htmlFor="amount">Amount</label>
<input
id="amount"
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
required
/>
</div>
<button type="submit" disabled={loading}>
{loading ? 'Generating...' : 'Generate Invoice PDF'}
</button>
{error && <p style={{ color: 'red' }}>{error}</p>}
{downloadUrl && (
<p>
<a href={downloadUrl} target="_blank" rel="noopener noreferrer">
Download Invoice PDF
</a>
</p>
)}
</form>
);
}
Step 4: Add the Page
Create a page that renders the form:
app/invoices/page.tsx
import { InvoiceForm } from './invoice-form';
export default function InvoicesPage() {
return (
<main>
<h1>Invoice Generator</h1>
<InvoiceForm />
</main>
);
}
Run npm run dev and visit http://localhost:3000/invoices to test.
Next Steps
- Integrate with React for non-Next.js React apps
- Batch generate documents for multiple records
- Generate Excel reports from templates