Splitting PDF documents is a common requirement in web applications. For example, you may need to divide a long report into single-page files, extract the cover page separately, or separate the first page from the remaining pages for further processing. Instead of uploading files to a server, you can perform these operations directly in a React application with JavaScript.

Spire.PDF for JavaScript enables developers to load, manipulate, and save PDF documents in browser-based applications. It works with WebAssembly and a virtual file system, allowing PDF files to be processed on the client side.

This article demonstrates how to split PDF documents using JavaScript in React with Spire.PDF for JavaScript.

Install Spire.PDF for JavaScript in a React Project

Open a terminal in the root directory of your React project and install the spire.office package:

npm i spire.office

After the installation is complete, copy the following runtime files and folder from the installed package to the React project's public folder:

public/
├── _framework/
├── spire.pdf.js
├── Spire.Pdf.Wasm.zip
├── spire.common.js
└── Spire.Common.Wasm.zip

The JavaScript loader, WebAssembly resources, and supporting framework files must remain accessible as static assets when the application runs. For detailed setup instructions and the exact integration process, see How to Integrate Spire.PDF for JavaScript in a React Project.

In the examples below, the PDF file to be split is named input.pdf. Place this file in the public folder as well, so that it can be loaded in the React application.

public/
├── input.pdf
└── ...

Split a PDF into Individual Files by Page in JavaScript

If you want to split a PDF document into multiple single-page PDF files, you can use the Split() method. This method separates the original PDF into individual documents and saves each page as a new PDF file.

The following example loads input.pdf, splits it page by page, and downloads each generated PDF file in the browser.

import React, { useState, useEffect } from 'react';

function App() {
  const [wasmModule, setWasmModule] = useState(null);

  useEffect(() => {
    (async () => {
      const publicUrl = process.env.PUBLIC_URL || '';
      const spireModule = await import(/* webpackIgnore: true */ `${publicUrl}/spire.pdf.js`);
      const rawModule = spireModule.default || spireModule;
      window.wasmModule = typeof rawModule === 'function'
        ? await rawModule({ locateFile: p => p.endsWith('.wasm') ? `${publicUrl}/${p}` : p })
        : rawModule;
      setWasmModule(window.wasmModule);
    })();
  }, []);

  const loadPdfToVfs = async (fileName) => {
    const publicUrl = process.env.PUBLIC_URL || '';
    const response = await fetch(`${publicUrl}/${fileName}`);
    const fileBytes = new Uint8Array(await response.arrayBuffer());
    window.dotnetRuntime.Module.FS.writeFile(fileName, fileBytes, { flags: 'w+' });
    return fileName;
  };

  const SplitPdf = async () => {
    const wasmModule = window.wasmModule?.spirepdf;
    if (!wasmModule) return;

    const inputFile = await loadPdfToVfs('input.pdf');

    const doc = new wasmModule.PdfDocument();
    doc.LoadFromFile(inputFile);

    const pageCount = doc.Pages.Count;
    const outFileName = 'SplitDocument_result-{0}.pdf';
    doc.Split(outFileName);

    for (let i = 0; i < pageCount; i++) {
      const splitFileName = `SplitDocument_result-${i}.pdf`;
      const fileArray = window.dotnetRuntime.Module.FS.readFile(splitFileName);
      const file = new Blob([fileArray], { type: 'application/pdf' });
      const url = URL.createObjectURL(file);
      const a = document.createElement('a');
      a.href = url;
      a.download = splitFileName;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    }
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Split PDF Documents in React</h1>
      <button onClick={SplitPdf} disabled={!wasmModule}>
        Split PDF
      </button>
    </div>
  );
}

export default App;

Output:

Split PDF by Each Page

Code Explanation

The code first imports and initializes the Spire.PDF WebAssembly module when the React component is mounted.

const spireModule = await import(/* webpackIgnore: true */ `${publicUrl}/spire.pdf.js`);

Then, the loadPdfToVfs() function loads input.pdf from the public folder and writes it into the virtual file system:

window.dotnetRuntime.Module.FS.writeFile(fileName, fileBytes, { flags: 'w+' });

After the PDF file is loaded, a PdfDocument object is created and the source PDF is opened:

const doc = new wasmModule.PdfDocument();
doc.LoadFromFile(inputFile);

The Split() method splits the PDF document into separate PDF files. The {0} placeholder in the output file name is replaced by the page index:

const outFileName = 'SplitDocument_result-{0}.pdf';
doc.Split(outFileName);

Finally, the generated PDF files are read from the virtual file system, converted into Blob objects, and downloaded in the browser.

Split a PDF by Page Range in JavaScript

In some cases, you may not want to split every page into a separate file. Instead, you may want to extract one page as an individual PDF and save the remaining pages as another PDF. This can be done by creating new PdfDocument objects and inserting selected pages from the source document.

The following example splits input.pdf into two files:

  • Split-1.pdf: contains the first page
  • Split-2.pdf: contains the remaining pages
import React, { useState, useEffect } from 'react';

function App() {
  const [wasmModule, setWasmModule] = useState(null);

  useEffect(() => {
    (async () => {
      const publicUrl = process.env.PUBLIC_URL || '';
      const spireModule = await import(/* webpackIgnore: true */ `${publicUrl}/spire.pdf.js`);
      const rawModule = spireModule.default || spireModule;
      window.wasmModule = typeof rawModule === 'function'
        ? await rawModule({ locateFile: p => p.endsWith('.wasm') ? `${publicUrl}/${p}` : p })
        : rawModule;
      setWasmModule(window.wasmModule);
    })();
  }, []);

  const loadPdfToVfs = async (fileName) => {
    const publicUrl = process.env.PUBLIC_URL || '';
    const response = await fetch(`${publicUrl}/${fileName}`);
    const fileBytes = new Uint8Array(await response.arrayBuffer());
    window.dotnetRuntime.Module.FS.writeFile(fileName, fileBytes, { flags: 'w+' });
    return fileName;
  };

  const SplitPdf = async () => {
    const wasmModule = window.wasmModule?.spirepdf;
    if (!wasmModule) return;

    const inputFile = await loadPdfToVfs('input.pdf');

    const doc = new wasmModule.PdfDocument();
    doc.LoadFromFile(inputFile);

    const newDoc1 = new wasmModule.PdfDocument();
    const newDoc2 = new wasmModule.PdfDocument();

    newDoc1.InsertPage(doc, 0);
    newDoc2.InsertPageRange(doc, 1, doc.Pages.Count - 1);

    newDoc1.SaveToFile('Split-1.pdf');
    newDoc2.SaveToFile('Split-2.pdf');

    for (const splitFileName of ['Split-1.pdf', 'Split-2.pdf']) {
      const fileArray = window.dotnetRuntime.Module.FS.readFile(splitFileName);
      const file = new Blob([fileArray], { type: 'application/pdf' });
      const url = URL.createObjectURL(file);
      const a = document.createElement('a');
      a.href = url;
      a.download = splitFileName;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    }
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Split PDF Documents in React</h1>
      <button onClick={SplitPdf} disabled={!wasmModule}>
        Split PDF
      </button>
    </div>
  );
}

export default App;

Output:

Split PDF by Page Range

Code Explanation

This example also starts by loading the source PDF file into the virtual file system and opening it with PdfDocument.

const doc = new wasmModule.PdfDocument();
doc.LoadFromFile(inputFile);

Then, two new PDF documents are created:

const newDoc1 = new wasmModule.PdfDocument();
const newDoc2 = new wasmModule.PdfDocument();

The first page of the source PDF is inserted into newDoc1:

newDoc1.InsertPage(doc, 0);

The remaining pages are inserted into newDoc2 with InsertPageRange():

newDoc2.InsertPageRange(doc, 1, doc.Pages.Count - 1);

Here, page indexes are zero-based. The number 1 means the second page of the original PDF, and doc.Pages.Count - 1 means all remaining pages after the first page.

After inserting the selected pages, the two new PDF documents are saved:

newDoc1.SaveToFile('Split-1.pdf');
newDoc2.SaveToFile('Split-2.pdf');

Finally, both output files are read from the virtual file system and downloaded to the local computer.

Conclusion

This article demonstrated how to split PDF documents in a React application using Spire.PDF for JavaScript. With the Split() method, you can divide a PDF into separate single-page documents. With InsertPage() and InsertPageRange(), you can extract specific pages or page ranges into new PDF files.

These methods are useful for building browser-based PDF tools, document management systems, online file-processing applications, and other React applications that require PDF splitting without server-side processing.

FAQs

Can I split a PDF into one file per page in React?

Yes. You can use the Split() method provided by Spire.PDF for JavaScript to split a PDF document into separate PDF files. Each page of the original PDF will be saved as an individual PDF file.

Can I split only a specific page range from a PDF?

Yes. Instead of splitting every page, you can create a new PdfDocument object and use InsertPage() or InsertPageRange() to copy selected pages from the source PDF into a new PDF document.

Are page indexes zero-based in Spire.PDF for JavaScript?

Yes. Page indexes start from 0. For example, page index 0 refers to the first page, and page index 1 refers to the second page. When using InsertPageRange(), make sure the start index and page count are set correctly.

Do I need a server to split PDF documents in React?

No. With Spire.PDF for JavaScript, the PDF can be loaded, processed, and saved in the browser using WebAssembly and the virtual file system. This makes it possible to split PDF documents directly in a React application without sending the file to a server.

Published in Document Operation

Combining PDF files is a common requirement in document management applications. For example, a React application may need to assemble invoices, reports, contracts, or scanned pages into a single PDF before the file is archived or shared. When the source documents do not need to be uploaded to a server, performing the operation in the browser can also simplify the workflow.

In this tutorial, you will learn how to merge PDF documents in a React application using Spire.PDF for JavaScript. The first example combines several complete PDF files in one operation. The second example provides more precise control by taking selected pages from different PDFs and adding them to a new document.

On this page:

Install Spire.PDF for JavaScript in a React Project

Open a terminal in the root directory of your React project and install the spire.office package:

npm i spire.office

After the installation is complete, copy the following runtime files and folder from the installed package to the React project's public folder:

public/
├── _framework/
├── spire.pdf.js
├── Spire.Pdf.Wasm.zip
├── spire.common.js
└── Spire.Common.Wasm.zip

The JavaScript loader, WebAssembly resources, and supporting framework files must remain accessible as static assets when the application runs. For detailed setup instructions and the exact integration process, see How to Integrate Spire.PDF for JavaScript in a React Project.

For the examples in this article, also place the input PDF files in the public folder so that the application can retrieve them with fetch():

public/
├── input_1.pdf
├── input_2.pdf
├── input_3.pdf
└── ...

Merge Multiple PDF Documents in React

If every page in every source file should appear in the result, the most direct approach is to use the PdfMerger.Merge() method. It accepts an array of input file paths, merges the files in the order in which they appear in the array, and writes the result to the WebAssembly virtual file system.

The following React component merges input_1.pdf, input_2.pdf, and input_3.pdf into a single document named MergedPdf.pdf:

import React, { useState, useEffect } from 'react';

function App() {
  const [wasmModule, setWasmModule] = useState(null);
  const [isGenerating, setIsGenerating] = useState(false);
  const [errorMessage, setErrorMessage] = useState('');

  useEffect(() => {
    (async () => {
      try {
        const publicUrl = process.env.PUBLIC_URL || '';
        const spireModule = await import(/* webpackIgnore: true */ `${publicUrl}/spire.pdf.js`);
        const rawModule = spireModule.default || spireModule;
        window.wasmModule = typeof rawModule === 'function'
          ? await rawModule({ locateFile: p => p.endsWith('.wasm') ? `${publicUrl}/${p}` : p })
          : rawModule;
        setWasmModule(window.wasmModule);
      } catch (error) {
        console.error('Failed to load spire.pdf.js:', error);
      }
    })();
  }, []);

  const loadPdfToVfs = async (fileName) => {
    const publicUrl = process.env.PUBLIC_URL || '';
    const response = await fetch(`${publicUrl}/${fileName}`);

    if (!response.ok) {
      throw new Error(`Failed to load ${fileName}: ${response.status} ${response.statusText}`);
    }

    const fileBytes = new Uint8Array(await response.arrayBuffer());
    const pdfHeader = String.fromCharCode(...fileBytes.slice(0, 4));

    if (pdfHeader !== '%PDF') {
      throw new Error(`${fileName} was loaded, but it is not a valid PDF file.`);
    }

    window.dotnetRuntime.Module.FS.writeFile(fileName, fileBytes, { flags: 'w+' });
    return fileName;
  };

  const MergePdfs = async () => {
    const wasmModule = window.wasmModule?.spirepdf;
    if (!wasmModule || isGenerating) {
      return;
    }

    setIsGenerating(true);
    setErrorMessage('');

    try {
      const inputFiles = await Promise.all([
        loadPdfToVfs('input_1.pdf'),
        loadPdfToVfs('input_2.pdf'),
        loadPdfToVfs('input_3.pdf'),
      ]);

      const outputFileName = 'MergedPdf.pdf';
      const mergeOp = new wasmModule.MergerOptions();
      wasmModule.PdfMerger.Merge({
        inputFiles,
        outputFile: outputFileName,
        pdfMergeOptions: mergeOp
      });

      const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
      const modifiedFile = new Blob([modifiedFileArray], { type: 'application/pdf' });
      const url = URL.createObjectURL(modifiedFile);
      const a = document.createElement('a');

      a.href = url;
      a.download = outputFileName;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    } catch (error) {
      console.error('Failed to merge PDFs:', error);
      setErrorMessage(error.message || 'Failed to merge PDFs.');
    } finally {
      setIsGenerating(false);
    }
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Merge PDF Documents in React</h1>
      <button onClick={MergePdfs} disabled={!wasmModule || isGenerating}>
        {isGenerating ? 'Generating...' : 'Generate'}
      </button>
      {errorMessage && <p style={{ color: 'crimson' }}>{errorMessage}</p>}
    </div>
  );
}

export default App;

Output:

Merge Multiple PDF Documents

How the Code Works

The component first loads spire.pdf.js inside useEffect(). Because the module is initialized asynchronously, the Generate button remains disabled until the runtime is ready.

The loadPdfToVfs() function then performs three tasks for each source document:

  1. It retrieves the PDF from the public directory with fetch().
  2. It checks the first four bytes for the %PDF signature to help catch missing files or non-PDF responses.
  3. It writes the file bytes to the WebAssembly virtual file system, where Spire.PDF can access them.

After all three files have been loaded, PdfMerger.Merge() combines them in the order specified by inputFiles. The output is read from the virtual file system, converted to a PDF Blob, and downloaded through a temporary object URL.

To change the merge order, simply rearrange the entries in the array. For example, the following order would place input_3.pdf first:

const inputFiles = await Promise.all([
  loadPdfToVfs('input_3.pdf'),
  loadPdfToVfs('input_1.pdf'),
  loadPdfToVfs('input_2.pdf'),
]);

Merge Selected Pages from Different PDF Documents in React

Merging complete documents is not always necessary. You may instead need to create a new PDF from a cover page in one file and a page range in another file. In this situation, load the source files as PdfDocument objects and use InsertPage() and InsertPageRange() to construct the output document.

The following example takes the first page from input_1.pdf, appends every page from input_2.pdf, and saves the selected content as MergedPdf.pdf:

import React, { useState, useEffect } from 'react';

function App() {
  const [wasmModule, setWasmModule] = useState(null);
  const [isGenerating, setIsGenerating] = useState(false);
  const [errorMessage, setErrorMessage] = useState('');

  useEffect(() => {
    (async () => {
      try {
        const publicUrl = process.env.PUBLIC_URL || '';
        const spireModule = await import(/* webpackIgnore: true */ `${publicUrl}/spire.pdf.js`);
        const rawModule = spireModule.default || spireModule;
        window.wasmModule = typeof rawModule === 'function'
          ? await rawModule({ locateFile: p => p.endsWith('.wasm') ? `${publicUrl}/${p}` : p })
          : rawModule;
        setWasmModule(window.wasmModule);
      } catch (error) {
        console.error('Failed to load spire.pdf.js:', error);
      }
    })();
  }, []);

  const loadPdfToVfs = async (fileName) => {
    const publicUrl = process.env.PUBLIC_URL || '';
    const response = await fetch(`${publicUrl}/${fileName}`);

    if (!response.ok) {
      throw new Error(`Failed to load ${fileName}: ${response.status} ${response.statusText}`);
    }

    const fileBytes = new Uint8Array(await response.arrayBuffer());
    const pdfHeader = String.fromCharCode(...fileBytes.slice(0, 4));

    if (pdfHeader !== '%PDF') {
      throw new Error(`${fileName} was loaded, but it is not a valid PDF file.`);
    }

    window.dotnetRuntime.Module.FS.writeFile(fileName, fileBytes, { flags: 'w+' });
    return fileName;
  };

  const MergePdfs = async () => {
    const wasmModule = window.wasmModule?.spirepdf;
    if (!wasmModule || isGenerating) {
      return;
    }

    setIsGenerating(true);
    setErrorMessage('');

    try {
      const [firstInputFile, secondInputFile] = await Promise.all([
        loadPdfToVfs('input_1.pdf'),
        loadPdfToVfs('input_2.pdf'),
      ]);

      const outputFileName = 'MergedPdf.pdf';
      const firstDocument = new wasmModule.PdfDocument();
      const secondDocument = new wasmModule.PdfDocument();
      const mergedDocument = new wasmModule.PdfDocument();

      firstDocument.LoadFromFile({ fileName: firstInputFile });
      secondDocument.LoadFromFile({ fileName: secondInputFile });

      if (firstDocument.Pages.Count < 1) {
        throw new Error('The first PDF does not contain any pages.');
      }

      if (secondDocument.Pages.Count < 1) {
        throw new Error('The second PDF does not contain any pages.');
      }

      mergedDocument.InsertPage({ ldDoc: firstDocument, pageIndex: 0 });
      mergedDocument.InsertPageRange(secondDocument, 0, secondDocument.Pages.Count - 1);
      mergedDocument.SaveToFile({ fileName: outputFileName });

      const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
      const modifiedFile = new Blob([modifiedFileArray], { type: 'application/pdf' });
      const url = URL.createObjectURL(modifiedFile);
      const a = document.createElement('a');

      a.href = url;
      a.download = outputFileName;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    } catch (error) {
      console.error('Failed to merge PDFs:', error);
      setErrorMessage(error.message || 'Failed to merge PDFs.');
    } finally {
      setIsGenerating(false);
    }
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Merge PDF Documents in React</h1>
      <button onClick={MergePdfs} disabled={!wasmModule || isGenerating}>
        {isGenerating ? 'Generating...' : 'Generate'}
      </button>
      {errorMessage && <p style={{ color: 'crimson' }}>{errorMessage}</p>}
    </div>
  );
}

export default App;

Output:

Merge Selected Pages from Different PDF Documents

Understanding the Page Selection Logic

The three PdfDocument instances have different roles:

  • firstDocument represents input_1.pdf.
  • secondDocument represents input_2.pdf.
  • mergedDocument is the new PDF that receives the selected pages.

PDF page indexes are zero-based in this example. Therefore, pageIndex: 0 refers to the first page:

mergedDocument.InsertPage({ ldDoc: firstDocument, pageIndex: 0 });

The following statement inserts a continuous range from secondDocument. Its start index is 0, while its end index is secondDocument.Pages.Count - 1, so the complete document is appended:

mergedDocument.InsertPageRange(
  secondDocument,
  0,
  secondDocument.Pages.Count - 1
);

You can change these indexes to merge only the pages required by your application. For instance, this statement inserts pages 2 through 5 from secondDocument because their zero-based indexes are 1 through 4:

mergedDocument.InsertPageRange(secondDocument, 1, 4);

Before using fixed page indexes, make sure the source document contains enough pages. The sample already checks for empty PDFs, but a production application should also validate user-supplied start and end indexes against Pages.Count.

Important Implementation Notes

Keep Runtime and Input Paths Correct

Files stored in the React public directory are requested by URL at runtime. The code uses process.env.PUBLIC_URL so it can construct paths correctly when the application is deployed under a non-root public path. A missing or incorrect file path may return an HTML error page instead of a PDF, which is why the sample verifies the %PDF header before writing the data to the virtual file system.

Wait for WebAssembly Initialization

Spire.PDF cannot process a document until its runtime has finished loading. The wasmModule state controls the button's disabled status, while isGenerating prevents the same operation from being started repeatedly before the current merge has finished.

Validate Page Ranges

When pages are chosen dynamically, check that the start and end indexes are non-negative, that the start index does not exceed the end index, and that both values fall within the source document's page count. This avoids invalid range errors and makes it easier to show a useful message in the React interface.

Release the Download URL

URL.createObjectURL() creates a temporary URL for the generated Blob. Calling URL.revokeObjectURL(url) after the download starts releases that URL and prevents it from remaining in browser memory longer than necessary.

Conclusion

Spire.PDF for JavaScript enables React applications to combine PDF content through a WebAssembly-based workflow. When all pages are required, PdfMerger.Merge() provides a concise way to merge several complete documents in a defined order. When the output must contain only specific content, PdfDocument, InsertPage(), and InsertPageRange() provide page-level control over the result.

With the runtime files configured in the public directory, these techniques can be integrated into document portals, reporting tools, contract workflows, and other React applications that need to assemble PDFs directly in the browser.

Published in Document Operation