Knowledgebase (2408)
Children categories
Intelligent Textbook Analysis and Automated Word Lesson Plan Generation with Spire.Agent.Office
2026-08-19 09:53:52 Written by Nina TangIn teaching work, lesson preparation is the most time-consuming and skill-demanding task for every teacher. When you receive a textbook, you need to read through each chapter and section, distill core knowledge points, organize the knowledge logic, and then design teaching objectives, determine teaching key and difficult points, arrange the complete teaching process of introduction — new teaching — consolidation — summary, and finally compile it into a standardized lesson plan. A complete lesson plan often takes several hours, and different teachers vary greatly in analysis depth and lesson plan structure, making it difficult to ensure consistent quality.
Comparison with Traditional SDK API Processing
| Traditional Spire.Office for .NET API | Spire.Agent.Office | |
|---|---|---|
| Driving approach | Write code to parse the textbook paragraph by paragraph: load document → iterate paragraphs → extract keywords → manually assemble the lesson plan; every step requires code control | Describe the parsing and generation goals in natural language, and AI automatically understands the textbook and generates the lesson plan |
| Code volume | Requires a large amount of code to maintain the knowledge point library, paragraph classification rules, and lesson plan template logic | Only configuration code + 1 natural language instruction |
| Lesson plan structure | Teaching objectives, key/difficult points, and teaching process must each be hard-coded with a set of generation logic | AI automatically generates a structurally complete lesson plan according to subject standards |
| Textbook understanding | Can only match mechanically by keywords, unable to understand the relationships and hierarchy between knowledge points | AI understands the textbook based on semantics, extracting chapter themes, test points, and teaching suggestions |
| Maintainability | Different subjects and textbook versions require separate development and maintenance | The analysis scope and lesson plan style can be adjusted at any time in natural language |
This article explains how to use the Word AI capability of Spire.Agent.Office to analyze textbooks and automatically generate lesson plans. Together, they form a complete lesson preparation pipeline: first use AI to parse the textbook PDF, organize unit key points and key/difficult points, then generate a standardized, content-complete lesson plan based on the analysis results.
For product installation and SpireToken configuration, refer to Integrating Spire.Agent.Office in a .NET Project. The examples below assume Spire.Agent.Office is installed and SpireToken is configured.
Intelligent Textbook Analysis
Intelligent textbook analysis is the starting point of the entire lesson preparation workflow, suitable for quickly establishing an overall understanding of the textbook before reading the whole book. The core idea is: pass the electronic textbook PDF as an attachment, let AI parse the textbook content, organize the core knowledge, key and difficult points, learning suggestions, and the connections between chapters according to the chapters, and generate a Word unit textbook analysis document. Teachers can use it to complete unit teaching planning, and subsequent lesson plan generation is also based on it.
using Spire.Agent.Office.AI;
using Spire.Agent.Office.Extensions;
using Spire.Doc;
// Textbook PDF (multiple chapters can be passed in)
string[] attachments = new string[] {
"E:\\Input\\Textbook-Rational_Numbers.pdf",
"E:\\Input\\Textbook-Addition_and_Subtraction_of_Algebraic_Expressions.pdf",
"E:\\Input\\Textbook-Linear_Equations_in_One_Variable.pdf"
};
// Save path
string savePath = "E:\\Output\\Textbook_Analysis.docx";
// SpireToken Key
string key = "**********************";
// Natural language instruction
string instruction =
"Please analyze the textbook content in the attached PDFs, and from the perspective of a lesson-preparing teacher, help me organize a textbook analysis suitable for daily lesson preparation.\n" +
"For each chapter, explain the chapter's core knowledge content, teaching key and difficult points, and recommended class hours.\n" +
"Try to preserve the key concepts and typical example points of each section, and supplement the common difficulties and error-prone points students encounter when learning this chapter.\n" +
"Also describe the connections between chapters. Please strictly analyze based on the actual content of the textbook in the PDFs and do not fabricate anything.\n" +
"Generate a Word document with a clear structure so that I can arrange the unit teaching plan accordingly, and subsequent lesson plans will also be based on this analysis.";
// Call the Word document processing function
AIResult result = ExecuteDemoWord(instruction, savePath, key, attachments);
// Execute Word document AI processing
static AIResult ExecuteDemoWord(string instruction, string savePath, string key, string[] attachments)
{
// Create an AIOptions configuration object
AIOptions options = new AIOptions();
// Set the SpireToken Key
options.SpireToken = key;
// Use the Document object to process the Word document
using (Document doc = new Document())
{
// Create the AI document processor
AIDocumentProcessor processor = doc.AI(options);
// Execute the AI instruction
return processor.ExecuteInstruction(doc, instruction, savePath, attachments);
}
}
AI-generated Word textbook analysis document 
The analysis document unfolds by chapter, clearly explaining each chapter's core knowledge, key and difficult points, and recommended class hours, and also supplements students' common learning difficulties, error-prone points, and the connections between chapters. Teachers only need to provide the electronic PDF of the textbook to complete the whole-book analysis, quickly identify key chapters, and reasonably allocate class hours; this unit textbook analysis can also be directly used as background material for the subsequent lesson plan generation.
Automated Word Lesson Plan Generation
Fine-grained lesson preparation for a single class can be further advanced on the basis of the textbook analysis in the first section. The core idea is: directly use the unit textbook analysis generated in the first section as input, and let AI generate a structurally complete, ready-to-use lesson plan based on the analysis of the relevant section, including student analysis, teaching objectives, teaching key and difficult points, teaching preparation, teaching process, blackboard design, and tiered after-class assignments.
using Spire.Agent.Office.AI;
using Spire.Agent.Office.Extensions;
using Spire.Doc;
// The unit textbook analysis generated in the first section (already includes the analysis of the relevant section content)
string inputPath = "E:\\Input\\Textbook_Analysis.docx";
// Save path
string savePath = "E:\\Output\\Linear_Equations_in_One_Variable-Lesson_Plan.docx";
// SpireToken Key
string key = "**********************";
// Natural language instruction
string instruction =
"Based on the content of the \"Linear Equations in One Variable\" section in the unit textbook analysis document, " +
"help me write a complete lesson plan Word document. It is recommended to include: " +
"student analysis, teaching objectives (knowledge and skills, process and methods, emotional attitude and values), teaching key and difficult points, teaching preparation, " +
"teaching process (introduction, new teaching, consolidation practice, class summary), blackboard design, and tiered after-class assignments. " +
"The teaching objectives and key/difficult points must closely match the textbook content, and the teaching process must be specific about how the teacher guides and how students learn in each segment. " +
"The after-class assignments should be tiered into basic and advanced questions. Please format according to a standardized lesson plan layout, unify the heading levels and fonts, and finally save and output in DOCX format";
// Call the Word document processing function
AIResult result = ExecuteDemoWord(instruction, inputPath, savePath, key, null);
// Execute Word document AI processing
static AIResult ExecuteDemoWord(string instruction, string inputPath, string savePath, string key, string[] attachmentPaths)
{
// Create an AIOptions configuration object
AIOptions options = new AIOptions();
// Set the SpireToken Key
options.SpireToken = key;
// Use the Document object to process the Word document
using (Document doc = new Document())
{
// Load the unit textbook analysis document as the context for lesson plan generation
if (!string.IsNullOrEmpty(inputPath) && File.Exists(inputPath))
{
doc.LoadFromFile(inputPath);
}
// Create the AI document processor
AIDocumentProcessor processor = doc.AI(options);
// Execute the AI instruction
return processor.ExecuteInstruction(doc, instruction, savePath, attachmentPaths);
}
}
AI-generated Word lesson plan document 
The generated lesson plan has a complete structure and content that closely matches the textbook, covering student analysis and tiered after-class assignments as well. Based directly on the unit textbook analysis, teachers can get a first draft of the lesson, then adjust and polish it, saving the time of writing from scratch. For multiple classes in the same unit, the same unit textbook analysis can be reused to generate lesson plans section by section and then proofread them uniformly, turning lesson preparation from "writing word by word" into "localized modification".
FAQ
Teaching objectives not matching the textbook content
Reason: AI's generation of teaching objectives depends on its understanding of the textbook theme. If the textbook content is too extensive or the instruction is too general, the objectives may diverge from the actual teaching content.
Solution: Limit the analysis scope in the instruction (such as specifying the chapter name), explicitly require the objectives to be developed from three dimensions, and bind the requirement "must be written based on the actual textbook content".
Lesson plan structure not standardized, missing sections
Reason: The section structure that the lesson plan should contain is not specified in the instruction, and the structure AI generates by default may not match the school template.
Solution: List the sections the lesson plan must include in order in the instruction (such as introduction, new teaching, consolidation, summary), and AI will output strictly according to this structure.
Analysis report missing test points or knowledge points
Reason: The textbook has too many chapters, or the same knowledge point is scattered across multiple chapters, making the analysis report incomplete.
Solution: Pass the complete textbook or the PDFs of relevant chapters as attachments, and specify the knowledge types to focus on in the instruction (such as "focus on frequently tested question types and examples").
Inconsistent formatting in the generated lesson plan
Reason: The layout requirements of the lesson plan are not specified in the instruction, and the heading levels, fonts, and paragraph styles output by AI may be inconsistent.
Solution: Add descriptions such as "format according to a standardized lesson plan layout and unify heading levels and fonts" to the instruction.
Get the SpireToken Key
- Contact sales@e-iceblue.com or visit https://www.e-iceblue.com/TemLicense.html to obtain a trial/commercial API key
Configure it in your code:
AIOptions options = new AIOptions();
options.SpireToken = key;
Hide, Unhide, and Control the Conversion of Excel Worksheets with JavaScript in React
2026-08-18 06:41:57 Written by Lisa LiIn daily work, we often need to hide some worksheets to simplify the interface display or protect sensitive data, and we can unhide them when necessary. In addition, when converting a workbook to HTML, you may also need to control whether hidden worksheets appear in the conversion result. Spire.XLS for JavaScript performs these operations directly in the browser based on WebAssembly, managing input and output files through the virtual file system (VFS), without any backend service support.
This article covers three core feature points:
- Hide a Worksheet
- Show a Hidden Worksheet
- Control Whether to Include Hidden Worksheets When Converting to HTML
For installation and project configuration, please refer to How to Integrate Spire.XLS for JavaScript in a React Project. The following examples assume that Spire.XLS is installed and the WebAssembly module has been initialized.
Hide a Worksheet
Hiding a worksheet is often used to simplify the display of a workbook or protect internal data. With Spire.XLS for JavaScript, you can hide a specified worksheet by setting the Visibility property of the worksheet object to WorksheetVisibility.Hidden.
function App() {
const hideSheet = async () => {
// Get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// Check if the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// Load the font and Excel file into VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'HideOrShowWorksheet.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// Load the workbook
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
// Get the worksheet named "Sheet1" and hide it
let sheet1 = workbook.Worksheets.get("Sheet1");
sheet1.Visibility = xlsModule.WorksheetVisibility.Hidden;
// Save the workbook
const outputFileName = "HideWorksheet_output.xlsx";
workbook.SaveToFile({ fileName: outputFileName });
// Dispose of the workbook object to release resources
workbook.Dispose();
// Read the converted file from VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Hide Worksheet</h1>
<button onClick={hideSheet}>
Start
</button>
</div>
);
}
export default App;
Original document (Sheet2 is already hidden)
Hide Sheet1 
Show a Hidden Worksheet
When you need to view or edit a hidden worksheet again, you can show it again by setting the Visibility property to WorksheetVisibility.Visible.
function App() {
const showSheet = async () => {
// Get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// Check if the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// Load the font and Excel file into VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'HideOrShowWorksheet.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// Load the workbook
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
// Get the second worksheet and set it as visible
let sheet2 = workbook.Worksheets.get(1);
sheet2.Visibility = xlsModule.WorksheetVisibility.Visible;
// Save the workbook
const outputFileName = "ShowWorksheet_output.xlsx";
workbook.SaveToFile({ fileName: outputFileName });
// Dispose of the workbook object to release resources
workbook.Dispose();
// Read the converted file from VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Show Worksheet</h1>
<button onClick={showSheet}>
Start
</button>
</div>
);
}
export default App;
Unhide Sheet2 
Control Whether to Include Hidden Worksheets When Converting to HTML
When converting to HTML, you can use the skipHideSheet parameter of the SaveToHtml method to control whether hidden worksheets are included in the conversion result. When set to false, the generated HTML includes hidden worksheets; when set to true, hidden worksheets are skipped and only visible worksheets remain in the HTML.
function App() {
const saveToHtml = async () => {
// Get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// Check if the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// Load the font and Excel file into VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'HideOrShowWorksheet.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// Load the workbook
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
// Hide the worksheet named "Sheet1"
let sheet1 = workbook.Worksheets.get("Sheet1");
sheet1.Visibility = xlsModule.WorksheetVisibility.Hidden;
// Set the output HTML file name
const result = "result.html";
// false --- Save HTML with hidden worksheets
// true --- Save HTML without hidden worksheets
workbook.SaveToHtml({
fileName: result,
skipHideSheet: false
});
// Dispose of the workbook object to release resources
workbook.Dispose();
// Read the converted file from VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(result);
const blob = new Blob([fileArray], { type: 'text/html' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = result;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Convert Workbook to HTML</h1>
<button onClick={saveToHtml}>
Start
</button>
</div>
);
}
export default App;
After conversion 
FAQ
The HTML conversion result contains extra worksheets
Cause: The original Excel document has multiple hidden worksheets. When the skipHideSheet parameter of SaveToHtml is set to false, all hidden worksheets appear in the conversion result.
Solution: You can use the following code to iterate through and check the hidden state of all sheets in the Excel file.
const sheetCount = workbook.Worksheets.Count;
for (let i = 0; i < sheetCount; i++) {
let sheet = workbook.Worksheets.get(i);
const visibility = sheet.Visibility;
}
Get a Free License
If you want to remove the evaluation message in the generated documents or get rid of functional limitations, please contact us to get a temporary license valid for 30 days.
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
- Merge Multiple PDF Documents in React
- Merge Selected Pages from Different PDF Documents in React
- Important Implementation Notes
- Conclusion
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:

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:
- It retrieves the PDF from the
publicdirectory withfetch(). - It checks the first four bytes for the
%PDFsignature to help catch missing files or non-PDF responses. - 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:

Understanding the Page Selection Logic
The three PdfDocument instances have different roles:
firstDocumentrepresentsinput_1.pdf.secondDocumentrepresentsinput_2.pdf.mergedDocumentis 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.