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"
}
๐๏ธ 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
Configure next.config.js
const nextConfig = {
i18n: {
locales: ['en', 'hi', 'mr', 'ta'],
defaultLocale: 'en',
},
};
๐ 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"',
},
});
}
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>
);
}
๐ 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!"
}
}
}
i18n Config: lib/i18n/config.ts
export const i18n = {
locales: ['en', 'hi', 'mr', 'ta'],
defaultLocale: 'en',
localeNames: {
en: 'English',
hi: 'เคนเคฟเคเคฆเฅ',
mr: 'เคฎเคฐเคพเค เฅ',
ta: 'เฎคเฎฎเฎฟเฎดเฏ',
},
};
๐จ 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>
๐ 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,
});
};
๐ Step 6: Deploy to Vercel
# Install Vercel CLI
npm i -g vercel
# Deploy
vercel
# Production
vercel --prod
Vercel Configuration
{
"regions": ["bom1"],
"functions": {
"src/app/api/**/*.ts": {
"memory": 1024,
"maxDuration": 10
}
}
}
๐ 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');
}
๐ Performance Optimizations
1. Code Splitting
Next.js handles this automatically with dynamic imports:
const MergePDF = dynamic(() => import('@/components/tools/MergePDF'));
2. Image Optimization
import Image from 'next/image';
<Image
src="/icon.png"
width={48}
height={48}
alt="PDF Icon"
/>
3. Caching
export const revalidate = 3600; // Cache for 1 hour
๐ฐ 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
- Next.js 14 App Router is perfect for i18n apps
- pdf-lib handles PDF manipulation efficiently
- Vercel's free tier is generous enough for thousands of users
- Multi-language support is crucial for Indian audience
- SEO brings 60% of traffic
๐ Links
- Live Site: pdfmitra.in
- GitHub: github.com/Praveennayak877/pdfmitra
๐ฌ Questions?
Drop a comment below! I'm happy to help with Next.js, PDF processing, or deployment questions.
Follow me for more tutorials!
- Website: pdfmitra.in
Top comments (0)