DEV Community

Cover image for Building PDFMitra: A Free PDF Tool with Next.js 14 (Complete Tech Guide) ๐Ÿš€
Praveen Nayak
Praveen Nayak

Posted on

Building PDFMitra: A Free PDF Tool with Next.js 14 (Complete Tech Guide) ๐Ÿš€

Building PDFMitra: A Free PDF Tool with Next.js 14 ๐Ÿš€

๐ŸŽฏ What We're Building

PDFMitra is a completely free PDF manipulation platform with multi-language support (Hindi, English, Marathi, Tamil). In this guide, I'll show you exactly how I built it.

Live Demo: pdfmitra.in

๐Ÿ“š Tech Stack

{
  "frontend": "Next.js 14 (App Router)",
  "language": "TypeScript",
  "styling": "Tailwind CSS",
  "pdf-processing": ["pdf-lib", "PDFKit"],
  "i18n": "react-i18next",
  "hosting": "Vercel (Free)",
  "analytics": "Google Analytics"
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ—๏ธ Project Structure

src/
โ”œโ”€โ”€ app/
โ”‚ โ”œโ”€โ”€ [locale]/ # Internationalized routes
โ”‚ โ”‚ โ”œโ”€โ”€ merge/
โ”‚ โ”‚ โ”œโ”€โ”€ split/
โ”‚ โ”‚ โ”œโ”€โ”€ compress/
โ”‚ โ”‚ โ””โ”€โ”€ page.tsx
โ”‚ โ””โ”€โ”€ api/ # Serverless functions
โ”‚ โ”œโ”€โ”€ merge/
โ”‚ โ”œโ”€โ”€ split/
โ”‚ โ””โ”€โ”€ compress/
โ”œโ”€โ”€ components/
โ”‚ โ”œโ”€โ”€ tools/ # Tool components
โ”‚ โ””โ”€โ”€ shared/ # Shared UI
โ””โ”€โ”€ lib/
โ”œโ”€โ”€ i18n/ # Translations
โ””โ”€โ”€ utils/ # PDF utilities

๐Ÿš€ Step 1: Setting Up Next.js with i18n

npx create-next-app@latest pdfmitra --typescript --tailwind --app
cd pdfmitra
npm install pdf-lib react-i18next i18next
Enter fullscreen mode Exit fullscreen mode

Configure next.config.js

const nextConfig = {
  i18n: {
    locales: ['en', 'hi', 'mr', 'ta'],
    defaultLocale: 'en',
  },
};
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“ Step 2: PDF Merge Implementation

API Route: app/api/merge/route.ts

import { NextRequest, NextResponse } from 'next/server';
import { PDFDocument } from 'pdf-lib';

export async function POST(request: NextRequest) {
  const formData = await request.formData();
  const files = formData.getAll('files') as File[];

  // Convert files to ArrayBuffer
  const pdfBuffers = await Promise.all(
    files.map(file => file.arrayBuffer())
  );

  // Merge PDFs
  const mergedPdf = await PDFDocument.create();

  for (const pdfBuffer of pdfBuffers) {
    const pdf = await PDFDocument.load(pdfBuffer);
    const copiedPages = await mergedPdf.copyPages(
      pdf, 
      pdf.getPageIndices()
    );
    copiedPages.forEach(page => mergedPdf.addPage(page));
  }

  const pdfBytes = await mergedPdf.save();

  return new NextResponse(pdfBytes, {
    headers: {
      'Content-Type': 'application/pdf',
      'Content-Disposition': 'attachment; filename="merged.pdf"',
    },
  });
}
Enter fullscreen mode Exit fullscreen mode

Frontend Component: components/tools/MergePDF.tsx

'use client';

import { useState } from 'react';

export function MergePDF() {
  const [files, setFiles] = useState<File[]>([]);
  const [isProcessing, setIsProcessing] = useState(false);

  const handleMerge = async () => {
    setIsProcessing(true);

    const formData = new FormData();
    files.forEach(file => formData.append('files', file));

    const response = await fetch('/api/merge', {
      method: 'POST',
      body: formData,
    });

    const blob = await response.blob();
    const url = URL.createObjectURL(blob);

    // Trigger download
    const link = document.createElement('a');
    link.href = url;
    link.download = 'merged.pdf';
    link.click();

    setIsProcessing(false);
  };

  return (
    <div>
      <input
        type="file"
        accept=".pdf"
        multiple
        onChange={(e) => setFiles(Array.from(e.target.files || []))}
      />
      <button 
        onClick={handleMerge}
        disabled={files.length < 2 || isProcessing}
      >
        {isProcessing ? 'Merging...' : 'Merge PDFs'}
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

๐ŸŒ Step 3: Multi-Language Support

Translation Structure: lib/i18n/translations/en.json

{
  "tools": {
    "merge": {
      "title": "Merge PDF",
      "description": "Combine multiple PDFs",
      "processing": "Merging PDFs...",
      "success": "PDFs merged successfully!"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

i18n Config: lib/i18n/config.ts

export const i18n = {
  locales: ['en', 'hi', 'mr', 'ta'],
  defaultLocale: 'en',
  localeNames: {
    en: 'English',
    hi: 'เคนเคฟเค‚เคฆเฅ€',
    mr: 'เคฎเคฐเคพเค เฅ€',
    ta: 'เฎคเฎฎเฎฟเฎดเฏ',
  },
};
Enter fullscreen mode Exit fullscreen mode

๐ŸŽจ Step 4: Beautiful UI with Tailwind

<button className="px-8 py-4 bg-gradient-to-r from-orange-500 to-green-500 text-white font-semibold rounded-lg hover:from-orange-600 hover:to-green-600 transition-all shadow-lg">
  Merge PDFs
</button>
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“Š Step 5: Google Analytics Integration

// lib/gtag.ts
export const GA_MEASUREMENT_ID = 'G-XXXXXXXXXX';

export const pageview = (url: string) => {
  window.gtag('config', GA_MEASUREMENT_ID, {
    page_path: url,
  });
};

export const event = ({ action, category, label, value }) => {
  window.gtag('event', action, {
    event_category: category,
    event_label: label,
    value: value,
  });
};
Enter fullscreen mode Exit fullscreen mode

๐Ÿš€ Step 6: Deploy to Vercel

# Install Vercel CLI
npm i -g vercel

# Deploy
vercel

# Production
vercel --prod
Enter fullscreen mode Exit fullscreen mode

Vercel Configuration

{
  "regions": ["bom1"],
  "functions": {
    "src/app/api/**/*.ts": {
      "memory": 1024,
      "maxDuration": 10
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ” Step 7: Security Best Practices

// File validation
const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB

if (file.size > MAX_FILE_SIZE) {
  throw new Error('File too large');
}

if (!file.type.includes('pdf')) {
  throw new Error('Invalid file type');
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“ˆ Performance Optimizations

1. Code Splitting

Next.js handles this automatically with dynamic imports:

const MergePDF = dynamic(() => import('@/components/tools/MergePDF'));
Enter fullscreen mode Exit fullscreen mode

2. Image Optimization

import Image from 'next/image';

<Image
  src="/icon.png"
  width={48}
  height={48}
  alt="PDF Icon"
/>
Enter fullscreen mode Exit fullscreen mode

3. Caching

export const revalidate = 3600; // Cache for 1 hour
Enter fullscreen mode Exit fullscreen mode

๐Ÿ’ฐ Cost Breakdown

Service Cost
Vercel Hosting $0
Domain (.in) โ‚น699/year
Total โ‚น699/year

๐Ÿ“Š Results After Launch

  • โœ… 500+ users in first week
  • โšก 2-3 second processing time
  • ๐Ÿ’ฐ $0 monthly costs
  • ๐ŸŒŸ 4.8/5 user rating

๐ŸŽฏ Key Takeaways

  1. Next.js 14 App Router is perfect for i18n apps
  2. pdf-lib handles PDF manipulation efficiently
  3. Vercel's free tier is generous enough for thousands of users
  4. Multi-language support is crucial for Indian audience
  5. SEO brings 60% of traffic

๐Ÿ”— Links

๐Ÿ’ฌ Questions?

Drop a comment below! I'm happy to help with Next.js, PDF processing, or deployment questions.


Follow me for more tutorials!

Top comments (0)