Building a Certificate Generator with React and TongoRender — TongoRender Blog
Back to Blog
tutorialscertificatereacttutorial

Building a Certificate Generator with React and TongoRender

Step-by-step tutorial for building a certificate generator in React. Design templates, collect form data, generate PDFs via API, and download them instantly.

TongoRender TeamFebruary 28, 202612 min

Certificates of completion, achievement awards, and participation diplomas are essential for online courses, workshops, conferences, and corporate training programs. Instead of designing each certificate manually in a tool like Canva, you can build a web application that generates them automatically from a template. In this tutorial, we will build a React-based certificate generator that produces professional PDF certificates using TongoRender.

What We Will Build

Our application will have three parts:

  1. A certificate template designed with HTML and CSS
  2. A React form for entering recipient details
  3. An API route that renders the certificate as a PDF

Step 1: Design the Certificate Template

Certificates need to look elegant. We use landscape A4 orientation with a decorative border, script fonts, and a formal layout:

// templates/certificate.js
export function certificateTemplate({ recipientName, courseName, completionDate, instructorName, certificateId }) {
  return `
  <!DOCTYPE html>
  <html>
  <head>
    <link href="https://fonts.googleapis.com/css2?family=Great+Vibes&family=Montserrat:wght@300;400;600&display=swap" rel="stylesheet">
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { width: 297mm; height: 210mm; font-family: 'Montserrat', sans-serif; }
      .certificate {
        width: 100%; height: 100%;
        border: 3px solid #1a365d;
        padding: 15mm;
        position: relative;
        background: linear-gradient(135deg, #fefefe 0%, #f7fafc 100%);
      }
      .inner-border {
        width: 100%; height: 100%;
        border: 1px solid #c9a84c;
        padding: 15mm;
        display: flex; flex-direction: column;
        align-items: center; justify-content: center;
        text-align: center;
      }
      .title { font-family: 'Great Vibes', cursive; font-size: 48pt; color: #1a365d; }
      .subtitle { font-size: 14pt; color: #4a5568; letter-spacing: 4px; text-transform: uppercase; margin: 10mm 0 5mm; }
      .recipient { font-family: 'Great Vibes', cursive; font-size: 36pt; color: #c9a84c; margin: 5mm 0; }
      .description { font-size: 12pt; color: #4a5568; max-width: 500px; line-height: 1.6; margin: 5mm 0; }
      .signatures { display: flex; justify-content: space-between; width: 80%; margin-top: 15mm; }
      .signature-block { text-align: center; }
      .signature-line { width: 150px; border-top: 1px solid #1a365d; margin-bottom: 5px; }
      .cert-id { position: absolute; bottom: 10mm; right: 15mm; font-size: 8pt; color: #a0aec0; }
    </style>
  </head>
  <body>
    <div class="certificate">
      <div class="inner-border">
        <div class="title">Certificate of Completion</div>
        <div class="subtitle">This is proudly presented to</div>
        <div class="recipient">${recipientName}</div>
        <div class="description">
          For successfully completing the course
          <strong>${courseName}</strong>
          on ${completionDate}.
        </div>
        <div class="signatures">
          <div class="signature-block">
            <div class="signature-line"></div>
            <div>${instructorName}</div>
            <div style="color:#718096;font-size:10pt">Instructor</div>
          </div>
          <div class="signature-block">
            <div class="signature-line"></div>
            <div>Academy Director</div>
            <div style="color:#718096;font-size:10pt">Authorized Signatory</div>
          </div>
        </div>
      </div>
      <div class="cert-id">Certificate ID: ${certificateId}</div>
    </div>
  </body>
  </html>`;
}

Step 2: Build the React Form

Create a form component that collects the required information:

import { useState } from 'react';

export default function CertificateForm() {
  const [formData, setFormData] = useState({
    recipientName: '', courseName: '', completionDate: '', instructorName: '',
  });
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);

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

    const blob = await response.blob();
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `certificate-${formData.recipientName.replace(/\s+/g, '-')}.pdf`;
    a.click();
    URL.revokeObjectURL(url);
    setLoading(false);
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-4 max-w-md mx-auto">
      <input placeholder="Recipient Name" required
        value={formData.recipientName}
        onChange={e => setFormData({...formData, recipientName: e.target.value})} />
      <input placeholder="Course Name" required
        value={formData.courseName}
        onChange={e => setFormData({...formData, courseName: e.target.value})} />
      <input type="date" required
        value={formData.completionDate}
        onChange={e => setFormData({...formData, completionDate: e.target.value})} />
      <input placeholder="Instructor Name" required
        value={formData.instructorName}
        onChange={e => setFormData({...formData, instructorName: e.target.value})} />
      <button type="submit" disabled={loading}>
        {loading ? 'Generating...' : 'Generate Certificate'}
      </button>
    </form>
  );
}

Step 3: Create the API Route

In your Next.js API route (or Express endpoint), render the certificate HTML as a landscape PDF:

// pages/api/generate-certificate.js (Next.js)
import { certificateTemplate } from '../../templates/certificate';
import { randomUUID } from 'crypto';

export default async function handler(req, res) {
  const { recipientName, courseName, completionDate, instructorName } = req.body;
  const certificateId = randomUUID().split('-')[0].toUpperCase();

  const html = certificateTemplate({
    recipientName, courseName, completionDate, instructorName, certificateId,
  });

  const pdfResponse = await fetch('https://api.tongorender.io/v1/pdf', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.TONGORENDER_API_KEY,
    },
    body: JSON.stringify({
      html,
      format: 'A4',
      landscape: true,
      margin: { top: '0mm', bottom: '0mm', left: '0mm', right: '0mm' },
      printBackground: true,
    }),
  });

  const pdfBuffer = Buffer.from(await pdfResponse.arrayBuffer());

  res.setHeader('Content-Type', 'application/pdf');
  res.setHeader('Content-Disposition', `attachment; filename="certificate-${certificateId}.pdf"`);
  res.send(pdfBuffer);
}

Step 4: Batch Generation

For courses with dozens or hundreds of students, generate certificates in batch from a CSV file:

const csv = require('csv-parse/sync');
const fs = require('fs');

async function batchGenerate(csvPath) {
  const records = csv.parse(fs.readFileSync(csvPath), { columns: true });

  for (const record of records) {
    const pdf = await generateCertificate(record);
    fs.writeFileSync(`certificates/${record.name.replace(/\s+/g, '-')}.pdf`, pdf);
    console.log(`Generated certificate for ${record.name}`);
  }
}

Design Tips for Certificates

  • Use decorative fonts sparingly — Script fonts like Great Vibes work for names and titles, but body text should be clean and readable.
  • Include a unique ID — Makes certificates verifiable. You can link the ID to a verification page on your site.
  • Print background colors — Set printBackground: true in the API options. Without this, background gradients and colors will be invisible in the PDF.
  • Test with long names — Some recipients have very long names. Use responsive font sizing or text truncation.
  • Add a QR code — Link it to a verification URL for added authenticity.

TongoRender renders web fonts, CSS gradients, and complex layouts faithfully, making it ideal for certificate generation. The zero-margin landscape mode gives you full control over the page design.

Build your certificate generator with TongoRender — 100 free renders per month, no credit card required.

Share this articleShare on Twitter