Knowledgebase (2597)
Children categories
A workbook exported by a reporting system usually carries nothing but the system name as its author, with the title, subject and keywords left blank; teams meanwhile need to attach fields the export does not produce — an export batch, an owner, an approval state — for archiving and searching. None of this takes up a cell: it all lives in the workbook's document properties, which Excel shows under "File > Info > Properties". Spire.XLS for JavaScript writes them in the browser on top of WebAssembly, using a virtual file system (VFS) for input and output files, so no back-end service is needed.
This article covers three feature points:
- Set the summary properties of a workbook
- Add custom properties
- Update the value of a custom property
For installation and project setup, see Integrating Spire.XLS for JavaScript in a React Project. The examples below assume Spire.XLS is installed and the WebAssembly module has been initialised.
Set the summary properties of a workbook
The summary properties are the layer of description Excel shows in File Explorer and under "File > Info", and the first thing a search of the archive picks up. A workbook produced by a program usually has only the system name as its author and leaves the other entries blank; filling them in gives the file a readable context as it moves between people. The text entries take a plain assignment, while the two date entries have to be given a date object. The steps are:
- Load the workbook and take the summary property collection from
workbook.DocumentProperties. - Assign the text entries —
Title,Subject,Author,Keywords,CommentsandCategory— directly. - Assign
CompanyandManagerthe same way; Excel carries these two in its extended properties. - Set
CreatedTimeandLastSaveTimetoDateobjects. - Save the workbook.
Here is a complete code example showing how to set the summary properties of a workbook in React:
function App() {
const setSummaryProperties = async () => {
// Get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// Check that the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// Load the font into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// Load the Excel file into the VFS
const inputFileName = 'WorkbookProperties.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// Load the workbook
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile(inputFileName);
// Set the text entries of the summary properties
const summary = workbook.DocumentProperties;
summary.Title = 'Q3 2026 Sales Report';
summary.Subject = 'Quarterly results by sales department';
summary.Author = 'E-iceblue';
summary.Keywords = 'sales, report, Excel';
summary.Comments = 'Summary filled in after the export';
summary.Category = 'Sales Report';
// Set the entries carried by the extended properties
summary.Company = 'E-iceblue';
summary.Manager = 'Sales Manager';
// Set the document dates; a Date object is required here
summary.CreatedTime = new Date(2026, 8, 1);
summary.LastSaveTime = new Date(2026, 8, 20);
// Save the workbook
const outputFileName = 'SetSummaryProperties.xlsx';
workbook.SaveToFile(outputFileName);
// Release resources
workbook.Dispose();
// Read the result file back out of the VFS and download it
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>Set workbook summary properties</h1>
<button onClick={setSummaryProperties}>Start</button>
</div>
);
}
export default App;
The effect of setting the summary properties of a workbook:

Add custom properties
Custom properties carry the business fields the summary properties have no room for, such as an export batch, a contact phone number, a revision number or an approval date. Unlike the summary properties they have no fixed set of entries: the caller decides the name and the type, and Excel accepts text, integer, decimal, boolean and date-and-time values. The steps are:
- Load the workbook and take the custom property collection from
workbook.CustomDocumentProperties. - Call
Addto append a property; the name and the value can be written as a named object such as{ strName, boolValue }, or passed directly as two arguments. - Pick the member that matches the type of the value:
intValuefor an integer,dblValuefor a decimal. - Pass a
Dateobject asdtValuefor a date-and-time value. - Save the workbook.
Here is a complete code example showing how to add custom properties to a workbook in React:
function App() {
const addCustomProperties = async () => {
// Get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// Check that the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// Load the font into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// Load the Excel file into the VFS
const inputFileName = 'WorkbookProperties.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// Load the workbook
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile(inputFileName);
// Add a boolean property; _MarkAsFinal marks the document as final
workbook.CustomDocumentProperties.Add({ strName: '_MarkAsFinal', boolValue: true });
// Add a text property; a name and a value can also be passed directly
workbook.CustomDocumentProperties.Add('The Editor', 'E-iceblue');
// Add an integer property
workbook.CustomDocumentProperties.Add({ strName: 'Phone number', intValue: 81705109 });
// Add a decimal property
workbook.CustomDocumentProperties.Add({ strName: 'Revision number', dblValue: 7.12 });
// Add a date and time property
workbook.CustomDocumentProperties.Add({ strName: 'Revision date', dtValue: new Date(2026, 8, 1) });
// Save the workbook
const outputFileName = 'AddCustomProperties.xlsx';
workbook.SaveToFile(outputFileName);
// Release resources
workbook.Dispose();
// Read the result file back out of the VFS and download it
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>Add custom properties</h1>
<button onClick={addCustomProperties}>Start</button>
</div>
);
}
export default App;
The effect of adding custom properties:

Update the value of a custom property
The value of a business field changes with the way it is counted: an export record count, say, has to be rewritten to a new figure once the data is topped up. Custom properties offer no member that assigns a value directly, but Add is keyed on the name — calling it again for a name that already exists does not append a duplicate entry, it replaces the value of the existing one. The steps are:
- Load the workbook and take the custom property collection from
workbook.CustomDocumentProperties. - Call
Addagain, passing the same name as the existing entry together with the new value. - Save the workbook.
Here is a complete code example showing how to update a custom property of a workbook in React:
function App() {
const updateCustomProperties = async () => {
// Get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// Check that the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// Load the font into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// Load the Excel file into the VFS
const inputFileName = 'WorkbookProperties.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// Load the workbook
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile(inputFileName);
// Rewrite the exported record count; adding an existing name overwrites it
workbook.CustomDocumentProperties.Add({ strName: 'ExportedRecords', intValue: 256 });
// Save the workbook
const outputFileName = 'UpdateCustomProperties.xlsx';
workbook.SaveToFile(outputFileName);
// Release resources
workbook.Dispose();
// Read the result file back out of the VFS and download it
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>Update the value of a custom property</h1>
<button onClick={updateCustomProperties}>Start</button>
</div>
);
}
export default App;
The effect of updating the value of a custom property:

FAQ
Why does assigning to CreatedTime throw "Assert failed: Value is not a Date"
Cause: CreatedTime and LastSaveTime take a JavaScript Date object and nothing else. A date string such as '2026-09-01', a timestamp number, or a hand-made object that only carries a toISOString() method all fail the type check and throw Assert failed: Value is not a Date.
Solution: build the Date with new Date(...) first, then assign it:
// 1 September 2026; months count from 0, so 8 means September
workbook.DocumentProperties.CreatedTime = new Date(2026, 8, 1);
Why does assigning to the Value of a custom property throw ArgumentNull_Generic
Cause: the Value property only reads. Assigning to it throws ArgumentNull_Generic Arg_ParamName_Name, value, and the original value does not change. Updating an existing property has to go through Add.
Solution: overwrite the original value with a same-name Add:
workbook.CustomDocumentProperties.Add({ strName: 'ExportedRecords', intValue: 256 });
Get a Free License
Spire.XLS for JavaScript offers a 30-day full-featured free trial license with no functional limitations. Apply here to evaluate before purchasing.
Add or Remove Cell Borders in Excel in React with JavaScript
2026-09-29 02:58:28 Written by liu taliaThe real dividers in a spreadsheet are not blank cells but borders: financial reports use lines of different weights to separate the header row from the totals, while data handed to a downstream system has to go out with the frames stripped off. Doing that by hand — selecting each block and opening Format Cells — is slow and hard to repeat. Spire.XLS for JavaScript does the same work in the browser on top of WebAssembly, using a virtual file system (VFS) for input and output files, so no back-end service is needed.
This article covers four feature points:
- Add a border to a selected cell or a cell range
- Add a border to the range that holds the data
- Add left, top, right, bottom and diagonal borders to a cell
- Remove the borders of a cell or a cell range
For installation and project setup, see Integrating Spire.XLS for JavaScript in a React Project. The examples below assume Spire.XLS is installed and the WebAssembly module has been initialised.
Add a border to a selected cell or a cell range
Given a table with no lines at all, the most direct approach is to put the frame back with BorderAround and BorderInside: the first one draws the outline, the second one the grid lines inside the range. Together they frame a whole block of data in one go, and they can also single out one cell — the header cell, say — so that it stands out from a sheet full of identical lines.
The steps are:
- Take the cell range you want to frame with
Range.get, by address - Call
BorderAroundto draw the outline, with the line style taken from theLineStyleTypeenumeration - Call
BorderInsideto add the separators between the cells inside the range - Take the header cell on its own and call
BorderAroundagain on it, this time with a medium line
The line style is passed in object notation, as { borderLine }.
function App() {
const addBorderToCells = async () => {
// Get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// Check that the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// Load the font and the input file into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'CellBorders.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// Load the workbook and take the first worksheet
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
const sheet = workbook.Worksheets.get(0);
// Take the B2:E6 range, frame it with thin lines and add thin inner lines
const dataRange = sheet.Range.get('B2:E6');
dataRange.BorderAround({ borderLine: xlsModule.LineStyleType.Thin });
dataRange.BorderInside({ borderLine: xlsModule.LineStyleType.Thin });
// Give the B2 header cell a medium border on all four sides
sheet.Range.get('B2').BorderAround({ borderLine: xlsModule.LineStyleType.Medium });
// Save the result file
const outputFileName = 'AddBorderToCells.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
// Dispose of the workbook object to free resources
workbook.Dispose();
// Read the result file back out of the VFS and download it
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>Add or Remove Cell Borders</h1>
<button onClick={addBorderToCells}>Add a border to a cell or range</button>
</div>
);
}
export default App;
The effect of adding a border to the data range and to the header cell:

Add a border to the range that holds the data
The previous section hard-codes the address B2:E6, which has to be edited as soon as rows or columns are added to the table. The AllocatedRange property returns the allocated range of a worksheet — the rectangle that actually holds the data — so the address maintains itself, and the outline can be given a different style from the header frame to set the table apart from the text around it.
The steps are:
- Get the range that holds the data through
AllocatedRange, with no address to maintain - Call
BorderAroundwith a medium dashed line, so the outline contrasts with the solid frames used elsewhere - Call
BorderInsideto add thin lines inside the range, keeping the rows separated
function App() {
const addBorderToDataRange = async () => {
const xlsModule = window.wasmModule?.spirexls;
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'CellBorders.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
const sheet = workbook.Worksheets.get(0);
// AllocatedRange returns the range that holds the data, so no address is needed
const dataRange = sheet.AllocatedRange;
// Give the outline a medium dashed border and the inside thin lines
dataRange.BorderAround({ borderLine: xlsModule.LineStyleType.MediumDashed });
dataRange.BorderInside({ borderLine: xlsModule.LineStyleType.Thin });
const outputFileName = 'AddBorderToDataRange.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
workbook.Dispose();
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>Add or Remove Cell Borders</h1>
<button onClick={addBorderToDataRange}>Add a border to the data range</button>
</div>
);
}
export default App;
The effect of adding a dashed outline and thin inner lines to the data range:

Add left, top, right, bottom and diagonal borders to a cell
BorderAround gives all four edges the same style, which is not enough when the requirement is "a thick red line on the left and a double line along the bottom". Handling the edges one at a time solves it: Borders.get takes a single edge by its BordersLineType member, and its LineStyle and Color can then be set independently, so every edge can have its own line style and colour. The two diagonal directions are available the same way.
The steps are:
- Take the cell to style with
Range.get - Take the left, top, right and bottom edges through
Borders.get(BordersLineType.EdgeLeft)and its siblings - Set
LineStyleon each edge, using a thick line, a dotted line, a slanted dash-dot line and a double line - Set
Coloron each edge, using red, brown, dark grey and orange red - Take another cell, fetch its diagonal-down edge with
BordersLineType.DiagonalDownand set its line style
function App() {
const addEdgeBorders = async () => {
const xlsModule = window.wasmModule?.spirexls;
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'CellBorders.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
const sheet = workbook.Worksheets.get(0);
// Give each of the four edges of B4 its own line style and colour
const cell = sheet.Range.get('B4');
const edgeSpecs = [
['EdgeLeft', xlsModule.LineStyleType.Thick, xlsModule.Color.get_Red()],
['EdgeTop', xlsModule.LineStyleType.Dotted, xlsModule.Color.get_Brown()],
['EdgeRight', xlsModule.LineStyleType.SlantedDashDot, xlsModule.Color.get_DarkGray()],
['EdgeBottom', xlsModule.LineStyleType.Double, xlsModule.Color.get_OrangeRed()],
];
for (const [edge, lineStyle, color] of edgeSpecs) {
const border = cell.Borders.get(xlsModule.BordersLineType[edge]);
border.LineStyle = lineStyle;
border.Color = color;
}
// Add a diagonal-down border to E6
const diagonal = sheet.Range.get('E6').Borders.get(xlsModule.BordersLineType.DiagonalDown);
diagonal.LineStyle = xlsModule.LineStyleType.Thin;
const outputFileName = 'AddEdgeBorders.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
workbook.Dispose();
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>Add or Remove Cell Borders</h1>
<button onClick={addEdgeBorders}>Set the four edges and a diagonal</button>
</div>
);
}
export default App;
The effect of styling the four edges of a cell and adding a diagonal line:

Remove the borders of a cell or a cell range
When an internal report goes out to a customer, or its data is fed to a downstream system, borders are often just noise: they make the data look like a finished report, and a parser that walks the sheet region by region can pick them up as content. Removing them is as simple as setting them — assign LineStyleType.None to the range's Borders.LineStyle and every line inside it goes at once.
The steps are:
- Take the worksheet that holds the framed report
- Take the cell range to clean up with
Range.get - Set the range's
Borders.LineStyletoLineStyleType.None, clearing every border inside it
function App() {
const removeBorders = async () => {
const xlsModule = window.wasmModule?.spirexls;
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'CellBorders.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
// The second worksheet holds the same report, already framed with borders
const sheet = workbook.Worksheets.get(1);
// Set every border in the B2:E6 range to None
sheet.Range.get('B2:E6').Borders.LineStyle = xlsModule.LineStyleType.None;
const outputFileName = 'RemoveBorders.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
workbook.Dispose();
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>Add or Remove Cell Borders</h1>
<button onClick={removeBorders}>Remove cell borders</button>
</div>
);
}
export default App;
The effect of clearing every border inside the range:

FAQ
Setting a top edge on a whole range puts a line above every row
Cause: Borders.get(BordersLineType.EdgeTop) returns the top edge of every cell in the range, not the outline of the range itself. Used on B2:E6 as it stands, every row inside the range gets a top edge of its own, so four extra lines appear in the middle of the table.
Solution: narrow the range to a single row or column, and the edge that comes back sits on the outline. Take the top from the first row, the bottom from the last row, the left from the first column and the right from the last column:
// Top: the first row only
sheet.Range.get('B2:E2').Borders.get(xlsModule.BordersLineType.EdgeTop).LineStyle = xlsModule.LineStyleType.Thin;
// Bottom: the last row only
sheet.Range.get('B6:E6').Borders.get(xlsModule.BordersLineType.EdgeBottom).LineStyle = xlsModule.LineStyleType.Thin;
// Left: the first column only
sheet.Range.get('B2:B6').Borders.get(xlsModule.BordersLineType.EdgeLeft).LineStyle = xlsModule.LineStyleType.Thin;
// Right: the last column only
sheet.Range.get('E2:E6').Borders.get(xlsModule.BordersLineType.EdgeRight).LineStyle = xlsModule.LineStyleType.Thin;
BorderInside throws when it is called on a single cell
Cause: BorderInside means "the separators inside the range", and a single cell has no inside, so passing one throws This method doesn't support for single cell. No result file is written either.
Solution: use BorderAround to frame a single cell, or set its edges one at a time as in the previous section:
sheet.Range.get('B2').BorderAround({ borderLine: xlsModule.LineStyleType.Thin });
Get a Free License
Spire.XLS for JavaScript offers a 30-day full-featured free trial license with no functional limitations. Apply here to evaluate before purchasing.
A product rename, a wording change, a company name typed wrong in a template — edits like these are awkward once they land in a PDF. When the source document is not at hand, the only options are to export the whole page as an image and lay text over it, or to delete the original text in a reader and type a replacement in by hand. Contracts and manuals make this worse: the same name can be scattered across body text, bullet lists and closing notes, and fixing each one by hand is slow and easy to miss.
Spire.PDF for JavaScript loads, edits and saves PDF documents in the browser on WebAssembly, so a rename is only a matter of locating the text and writing it back, with files read and written through a virtual file system (VFS) and no backend involved. The four sections below cover replacing the first match, replacing every match (together with three switches: recoloring, match mode and replacement area), covering the original word with a background color, and drawing a rectangle over the original before redrawing the new word. In the sample the old and new names are the same length, so the layout does not shift after replacement.
This article covers four core features:
- Replace the First Match
- Replace All Matches
- Replace by Covering
- Draw a Rectangle to Cover the Original and Redraw the Text
For installation and project configuration, see Integrate Spire.PDF for JavaScript into a React Project. The examples below assume Spire.PDF is installed and the WebAssembly module is initialized.
Replace the First Match
Spire.PDF for JavaScript provides the PdfTextReplacer class for turning text on a page into another piece of text. It is built per page, and ReplaceText replaces only the first match it finds, returning the number of replacements actually made (1 here); any remaining occurrences of the same name on the page are left as they are.
function App() {
const replaceFirstMatch = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the font into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// Load the PDF file to be processed into the VFS
const inputFileName = 'ProductOverview.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF document
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// Take the first page; the replacement is limited to this page
let page = doc.Pages.get_Item(0);
// Create the text replacer and replace only the first match
const replacer = new pdfModule.PdfTextReplacer(page);
replacer.ReplaceText('Stellar Drive', 'Nova Drive');
const outputFileName = 'ReplaceFirstMatch.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
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>Replace the First Match</h1>
<button onClick={replaceFirstMatch}>
Replace
</button>
</div>
);
}
export default App;
Only the first match is replaced:

Replace All Matches
Spire.PDF for JavaScript also provides PdfTextReplacer.ReplaceAllText(), which swaps every matching piece of text on the page in one go. It takes the same arguments as ReplaceText and likewise returns the number of replacements. It also has a three-argument overload, ReplaceAllText(old, new, color), which recolors the text as it replaces it.
function App() {
const replaceAllMatches = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the font into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// Load the PDF file to be processed into the VFS
const inputFileName = 'ProductOverview.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF document
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
let page = doc.Pages.get_Item(0);
// The third argument is a color, recoloring the new word as it is written
const replacer = new pdfModule.PdfTextReplacer(page);
replacer.ReplaceAllText('Stellar Drive', 'Nova Drive', pdfModule.Color.get_Red());
const outputFileName = 'ReplaceAllMatches.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
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>Replace All Matches</h1>
<button onClick={replaceAllMatches}>
Replace
</button>
</div>
);
}
export default App;
All old name are replaced, and the new name is recolored red at the same time:

Beyond recoloring,
PdfTextReplacer.Optionshas two more switches.ReplaceTypedecides what counts as a match: by default only an identical string is accepted, and it can be switched toIgnoreCase,WholeWordorRegex.SetReplacementArealimits the replacement to a rectangle, whichPdfTextFindercan work out first:
const replacer = new pdfModule.PdfTextReplacer(page);
// Ignore case
replacer.Options.ReplaceType = pdfModule.ReplaceActionType.IgnoreCase;
// Limit the replacement to a rectangle
const finds = new pdfModule.PdfTextFinder(page).Find('Stellar Drive');
replacer.Options.SetReplacementArea(finds.get(0).Bounds[0]);
// Call it once the options are set
const count = replacer.ReplaceAllText('Stellar Drive', 'Nova Drive');
Replace by Covering
Spire.PDF for JavaScript also provides PdfTextFragment.ApplyRecoverString(), which covers a located piece of text in place: it lays a background color over the original and then writes the new text in the same position. It spares you from working out coordinates and redrawing by hand. The price is that only the background color can be chosen: the new text is drawn with the font resolved from the VFS and keeps the original foreground color, and with a white background the seam is invisible. When the font and the color need to change as well, see the redrawing approach in the next section.
function App() {
const replaceByCovering = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the fonts used by the document into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// Load the PDF file to be processed into the VFS
const inputFileName = 'ProductOverview.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF document
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
let page = doc.Pages.get_Item(0);
// Locate the target text with a finder first
const finder = new pdfModule.PdfTextFinder(page);
const finds = finder.Find('Stellar Drive');
// Cover each hit in turn: the second argument is the background color,
// and the third one, true, writes the new text as Unicode
for (let i = 0; i < finds.length; i++) {
finds.get(i).ApplyRecoverString('Nova Drive', pdfModule.Color.get_White(), true);
}
const outputFileName = 'RecoverReplace.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
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>Replace by Covering</h1>
<button onClick={replaceByCovering}>
Replace
</button>
</div>
);
}
export default App;
A document where the original text is covered with a white background and the new word is written in its place:

Draw a Rectangle to Cover the Original and Redraw the Text
The first three sections all work on the text layer. This one takes a different tack: use PdfTextFinder to get the position of the keyword, draw a white rectangle to cover the original text, and then write the new text inside that rectangle. Compared with ApplyRecoverString, the extra freedom is that you choose the font, size and color yourself.
function App() {
const replaceByDrawing = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the font into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// Load the PDF file to be processed into the VFS
const inputFileName = 'ProductOverview.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF document
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
let page = doc.Pages.get_Item(0);
// Locate the target text with a finder first
const finder = new pdfModule.PdfTextFinder(page);
const finds = finder.Find('Stellar Drive');
// The white rectangle that covers the original, plus the new word's font and color
const white = pdfModule.PdfBrushes.get_White();
const FONT_SIZE = 9;
const font = new pdfModule.PdfTrueTypeFont({ fontFile: '/Library/Fonts/ARIAL.TTF', size: FONT_SIZE });
const brush = new pdfModule.PdfSolidBrush({ pdfRGBColor: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_DarkBlue() }) });
// Turn LineLimit off so the text is not clipped to the box
const format = new pdfModule.PdfStringFormat();
format.LineLimit = false;
// Set the alignment of the text
format.Alignment = pdfModule.PdfTextAlignment.Center;
format.LineAlignment = pdfModule.PdfVerticalAlignment.Middle;
for (let i = 0; i < finds.length; i++) {
const rec = finds.get(i).Bounds[0];
// Cover the original text with a white rectangle first
page.Canvas.DrawRectangle({ brush: white, rectangle: rec });
// Write the new word straight into the hit box
page.Canvas.DrawString({ s: 'Nova Drive', font: font, brush: brush, layoutRectangle: rec, format: format });
}
const outputFileName = 'DrawRectangleAndText.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
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>Draw a Rectangle to Cover the Original and Redraw the Text</h1>
<button onClick={replaceByDrawing}>
Replace
</button>
</div>
);
}
export default App;
A document where a white rectangle covers the original text and the new word is written directly inside the hit box:

FAQ
The old name can still be found and copied after replacement
Cause: ApplyRecoverString is a visual-layer operation — it lays a background color over the original text and draws the new word on top, while the original text object stays in the text layer, so searching or copying in a reader still returns the old name. Measured on the covering replacement, the occurrence count of the old word is unchanged while the new word gains four hits.
Fix: When a real replacement at the content level is needed, switch to PdfTextReplacer; in its output the old word disappears from the text layer and the new word enters it. Measured, the four occurrences of the old word drop to 0 and the new word rises from 1 to 5:
// Content-level replacement: the old text no longer remains
const replacer = new pdfModule.PdfTextReplacer(page);
replacer.ReplaceAllText('Stellar Drive', 'Nova Drive');
Following the official sample's white rectangle plus DrawString redraw, the white background appears but the new text does not
Cause: The official sample follows the same approach as the fourth section here — locate the position with PdfTextFinder, draw a white rectangle to cover the original text, then paint the new text with Canvas.DrawString. The white rectangle is fine, but the redraw step has a trap:
- The rectangle overload of
DrawStringis governed byPdfStringFormat.LineLimit, which defaults totrueand clips the text to the box; the hit box hugs the glyphs tightly, so if its height is 11 points (exactly the font size) it cannot hold a full line height, and the whole line is clipped away — the new text appears neither on the page nor in the text layer.
Fix: Pass a PdfStringFormat with LineLimit turned off and the hit box can be used as the layout box as it is (which is what the fourth section does).
const format = new pdfModule.PdfStringFormat();
format.LineLimit = false;
page.Canvas.DrawString({ s: 'Nova Drive', font: font, brush: brush, layoutRectangle: rec, format: format });
Get a Free License
If you want to remove the evaluation message from the result document, or get past the feature limits, contact sales for a 30-day temporary license.