Recommended Free Tools
Use a Next.js App Router Route Handler to accept the PDF, validate the requested page ranges, and use pdf-lib to copy those pages into a new document. The handler can return that document as a PDF response. For files that should never leave a user’s device, the same library can also run in the browser; the right location depends on file size, device capacity, access-control needs, and your host’s limits.
What splitting a PDF means
PDF splitting usually means extracting selected pages into one or more new PDF documents. A user might request pages 2–4 as one output, or pages 1, 3, and 7 as separate outputs. In either case, validate the selection against the source document’s page count, then copy the chosen pages into new documents.
pdf-lib supports loading PDFs and copying pages, and its project documentation says it works in browsers and Node.js without native dependencies. See the pdf-lib documentation. Next.js Route Handlers provide a public HTTP endpoint that can read request data and return non-UI responses, including files; see the Next.js backend-for-frontend guide.
Build a PDF-splitting endpoint with the App Router
Install the dependency
From your project directory, install pdf-lib with npm:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
#1 Best Overall
npm install pdf-lib
The example below uses TypeScript and the App Router. Create app/api/split/route.ts. It accepts a multipart form containing a field named file and a comma-separated ranges field. Ranges use human-readable, 1-based page numbers; for example, 1-3,5 means pages 1, 2, 3, and 5 in a single output PDF.
Route handler
import { PDFDocument } from 'pdf-lib';
export const runtime = 'nodejs';
const MAX_FILE_BYTES = 20 * 1024 * 1024;
const MAX_PAGES = 500;
function parsePages(input: string, pageCount: number): number[] {
const parts = input.split(',').map((part) => part.trim());
if (!parts.length || parts.some((part) => part === '')) {
throw new Error('Invalid page ranges');
}
const selected = new Set<number>();
for (const part of parts) {
const match = /^(d+)(?:-(d+))?$/.exec(part);
if (!match) throw new Error('Invalid page ranges');
const start = Number(match[1]);
const end = match[2] === undefined ? start : Number(match[2]);
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) ||
start < 1 || end < start || end > pageCount) {
throw new Error('Page range is out of bounds');
}
for (let page = start; page <= end; page++) selected.add(page - 1);
}
return [...selected].sort((a, b) => a - b);
}
export async function POST(request: Request) {
try {
const form = await request.formData();
const file = form.get('file');
const ranges = form.get('ranges');
if (!(file instanceof File) || typeof ranges !== 'string') {
return Response.json({ error: 'Provide a PDF file and page ranges.' }, { status: 400 });
}
if (file.size === 0 || file.size > MAX_FILE_BYTES) {
return Response.json({ error: 'File is empty or exceeds the upload limit.' }, { status: 413 });
}
if (file.type && file.type !== 'application/pdf') {
return Response.json({ error: 'Upload must be a PDF.' }, { status: 415 });
}
const source = await PDFDocument.load(await file.arrayBuffer());
const pageCount = source.getPageCount();
if (pageCount === 0 || pageCount > MAX_PAGES) {
return Response.json({ error: 'PDF has an unsupported page count.' }, { status: 422 });
}
const indices = parsePages(ranges, pageCount);
const output = await PDFDocument.create();
const pages = await output.copyPages(source, indices);
for (const page of pages) output.addPage(page);
const bytes = await output.save();
return new Response(new Uint8Array(bytes), {
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="split.pdf"',
'Cache-Control': 'no-store',
},
});
} catch {
return Response.json({ error: 'Could not process this PDF or page selection.' }, { status: 400 });
}
}
The 20 MiB upload and 500-page values are example application limits, not limits guaranteed by Next.js or pdf-lib. Choose them after checking your hosting provider’s request, memory, and execution constraints, and test representative documents. PDFDocument.load can reject input it cannot parse; do not promise compatibility with every encrypted, malformed, signed, or form-heavy PDF without testing the exact library version and files your app accepts.
Rank #2
- Full-featured professional audio and music editor that lets you record and edit music, voice and other audio recordings
- Add effects like echo, amplification, noise reduction, normalize, equalizer, envelope, reverb, echo, reverse and more
- Supports all popular audio formats including, wav, mp3, vox, gsm, wma, real audio, au, aif, flac, ogg and more
- Sound editing functions include cut, copy, paste, delete, insert, silence, auto-trim and more
- Integrated VST plugin support gives professionals access to thousands of additional tools and effects
Call the endpoint from a form
const form = new FormData();
form.append('file', fileInput.files[0]);
form.append('ranges', '1-3,5');
const response = await fetch('/api/split', { method: 'POST', body: form });
if (!response.ok) {
const error = await response.json();
throw new Error(error.error ?? 'PDF splitting failed');
}
const blob = await response.blob();
const downloadUrl = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = downloadUrl;
link.download = 'split.pdf';
link.click();
URL.revokeObjectURL(downloadUrl);
For production UI, show progress or a clear waiting state for large files, surface safe validation errors, and revoke the object URL after the browser has finished using it. The sample endpoint creates one PDF from all selected pages. If users need multiple output files, create one document per requested range and define a delivery method—such as a ZIP download or separate downloads—while accounting for the combined response size.
Validate uploads and page selections
Never treat the filename extension or browser-supplied MIME type as proof that the content is a valid PDF. The handler checks for a supplied file, a non-empty body, a size cap, and a parseable document. The MIME check is only an early rejection; parsing is still necessary. The Next.js guide says, “Never trust incoming request data. Validate content type and size, and sanitize against XSS before use.”
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #3
- Create a mix using audio, music and voice tracks and recordings.
- Customize your tracks with amazing effects and helpful editing tools.
- Use tools like the Beat Maker and Midi Creator.
- Work efficiently by using Bookmarks and tools like Effect Chain, which allow you to apply multiple effects at a time
- Use one of the many other NCH multimedia applications that are integrated with MixPad.
- Normalize page notation: define whether ranges are inclusive, whether repeated pages are allowed, and whether ordering follows the input or the document. The sample treats ranges as inclusive, removes duplicates, and returns pages in ascending order.
- Reject invalid bounds: pages start at 1 for the user, but
copyPagestakes 0-based indices. Reject zero, reversed ranges, non-numeric values, and endpoints beyond the document length before copying. - Limit work: a byte limit alone does not control CPU or memory use. Consider page-count limits, request timeouts, concurrency controls, authentication for restricted tools, and rate limits for public endpoints.
- Keep errors safe: return useful validation messages without exposing stack traces or sensitive details. Avoid logging file contents or unnecessary personal data.
Next.js recommends timeouts to protect resources, limiting sensitive data in responses and backend logs, and using dedicated services for user-generated static assets where appropriate. Treat this route as a public endpoint: implement authorization if access is restricted, and rate-limit it where abuse could consume resources.
Choose browser-side or server-side processing
| Consideration | Browser | Next.js server route |
|---|---|---|
| Does the source file leave the device? | Not if all processing stays local. | Yes; the upload is sent to your application. |
| Where validation and access control run | In client code; a user can modify client-side checks. | At a central endpoint, where server-side checks and authorization can be applied. |
| Resource constraints | Depends on device memory and responsiveness, especially on mobile. | Depends on the host’s request, memory, and execution limits. |
| File retention | No server retention is necessary if the whole workflow remains local. | Do not persist uploads unless the feature needs it; plan cleanup for temporary or stored files. |
pdf-lib supports both environments, but that does not establish a universal speed or safety winner. Browser processing can avoid sending a sensitive file to your server, while server processing gives you a central place for policy enforcement. Test memory use and responsiveness on your target devices, and check your deployment platform’s current limits before deciding.
Rank #4
Deployment, performance, and reliability
Do not assume a local file written during one request will be available to another. Next.js warns that some providers run Route Handlers as lambda functions; those handlers may not share data across requests, may lack writable filesystem access, and may be terminated when they exceed a timeout. For larger uploads or outputs, inspect the host’s current request-size, memory, execution-time, and storage limits. A direct browser upload to dedicated storage may be suitable when the host’s request path is too restrictive.
- Memory: the example reads the upload into memory and builds another PDF in memory. Large inputs and outputs can increase peak memory use; test with realistic documents rather than assuming the sample caps are appropriate.
- Latency: parsing and copying are work performed for each request. No universal timing or maximum file size is established by the cited documentation, so measure on your own deployment and device mix.
- Timeouts and bursts: set limits appropriate to your runtime and consider queuing work if jobs regularly exceed synchronous request windows. Limit concurrent processing to protect the service.
- Confidentiality: use HTTPS, avoid unnecessary persistence, keep logs free of document contents, and make retention and deletion behavior explicit if you store files.
- Output delivery: return a single PDF directly when that is the requested result. For multiple outputs, consider response size and whether a ZIP or separate downloads better fits the client and host.
Troubleshooting common failures
- “Provide a PDF file and page ranges.” The request is missing the
fileorrangesform field, or the file field is not a browserFile. Send multipart form data with those exact field names. - Upload rejected as too large or empty. Check the selected file and align the app’s size cap with the hosting provider’s request limit. Raising the app limit cannot override a smaller platform limit.
- “Upload must be a PDF.” The example rejects a non-empty MIME type other than
application/pdf. MIME metadata can be absent or inaccurate; if you adjust this check, retain parsing and size validation. - Invalid or out-of-bounds page range. Use inclusive 1-based notation such as
1-3,5, with endpoints within the source’s page count. The sample rejects reversed ranges and malformed input. - Could not process the PDF. The file may not be parseable by the installed version, or document loading or saving may have failed. Test with a known-good PDF and check server-side diagnostics without returning sensitive exception details to the client.
- Request times out or runs out of memory. Reduce allowed input size or page count, lower concurrency, move work to a suitable asynchronous architecture, or process locally in the browser. Check actual host limits before choosing a fix.
- Output cannot be downloaded. Confirm the response is successful and has
Content-Type: application/pdf; ensure the client reads the response as a blob rather than JSON.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server, not a PDF-splitting library; it does not replace the pdf-lib workflow above. If your task also needs website screenshots, one GET request can return an image or PDF. See the ScreenshotNeo API documentation.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Best Value
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo accepts cookie or consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; these steps can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, with response headers identifying the page verdict and billing status. Its MCP server provides screenshot and PDF-capture tools for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.
Sign up for ScreenshotNeo’s free plan.
Frequently Asked Questions
Does this endpoint split a PDF into one file per page?
No. The example collects the requested pages into one output PDF. Creating a separate PDF for each page requires creating and saving a new document for each selected page.
Can I use the same PDF library in a browser?
Yes. The pdf-lib project documentation states that it works in browsers as well as Node.js; choose browser-side processing when keeping the source file on-device is important.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




