When the same document has to be sent to multiple recipients, only a few elements change from one copy to the next, such as the recipient name, fax number and date, while the rest of the layout and wording remains identical. Word's mail merge turns this variable content into merge fields kept in a mail merge template; at run time, a set of data is filled in to generate the corresponding document. Compared with saving each copy separately on the desktop, performing the merge in the browser does not require recipients to install Office, and the data does not have to be uploaded to a server for processing.

Using Spire.Doc for JavaScript as an example, this article explains the structure of the merge fields in a mail merge template, how MailMerge.Execute() is called, and how to prepare a date string for a target culture before the merge. The entire process runs in the browser through WebAssembly, and both the template and the data are read from and written to the local virtual file system (VFS).

This article covers three core features:

For installation and project setup, refer to Integrating Spire.Doc for JavaScript in a React Project. The examples below assume Spire.Doc is installed and the WebAssembly module is initialized.


Prepare a Mail Merge Template with Merge Fields in Word

A mail merge template is an ordinary .docx document: the fixed content is laid out as usual, and the positions that change with the data are replaced by merge fields. A merge field is enclosed by a pair of field markers; the field code declares the field name with MERGEFIELD, and the field result area displays placeholder text in the form «field name», which is the text replaced during the mail merge.

The mail merge template used in this article is a "Requirement Confirmation" letter. The letterhead, document number, body text and confirmation table are all fixed content, and three merge fields are defined:

Merge Field Location Occurrences
Contact Name Recipient row, salutation, closing contact 3
Fax Fax row 1
Date Date row 1

Contact Name appears three times in the document. After the merge, all three occurrences take the same value, so it only has to be supplied once in the field value array.

The specific steps are as follows:

  1. Create a Word document and lay out the fixed content.
  2. Place the cursor where the variable content should go and press Ctrl + F9 to insert a pair of field markers.
  3. Enter the field code inside the markers, for example MERGEFIELD "Contact Name" \* MERGEFORMAT; enclose the field name in double quotes when it contains spaces.
  4. Press F9 to update the field. The position then displays placeholder text in the form «Contact Name».
  5. Insert the remaining merge fields at the other variable positions in the same way, save the document as .docx, and place it in the project's public directory, for example public/static/data/MailMerge.docx.

It is important to note that the placeholder text «field name» belongs to the field's result area, which lies between the separate and end field markers. If the angle brackets are typed manually outside the field, they do not belong to that field and are not replaced during the merge. Pressing Alt + F9 switches between the field code and the field result, which helps verify where the field markers begin and end.


Execute a Mail Merge

MailMerge.Execute(fieldNames, fieldValues) takes two arrays of equal length, whose elements correspond by index:

Parameter Description
fieldNames Array of field names, corresponding to the field names declared by MERGEFIELD in the template
fieldValues Array of field values, corresponding to fieldNames by index

Field name matching is case-insensitive. A field that exists in the template but is not listed in fieldNames is left blank in its result area after the merge.

The specific steps are as follows:

  1. Get the Spire.Doc WASM module and confirm that it is ready.
  2. Load the mail merge template and the font into the virtual file system (VFS).
  3. Create a Document object and load the template.
  4. Prepare the field name array and the field value array so that their order corresponds one to one.
  5. Call the Document.MailMerge.Execute() method to execute the mail merge.
  6. Save the result file with the Document.SaveToFile() method, read it back from the VFS, and trigger a browser download.

The code for executing a mail merge is as follows:

function App() {
  const MailMerge = async () => {
    // Get the Spire.Doc WASM module
    const docModule = window.wasmModule?.spiredoc;

    // Check if the module is ready
    if (!docModule) {
      alert('Spire.Doc is not ready yet');
      return;
    }

    // Load the mail merge template and the font
    let inputFileName = 'MailMerge.docx';
    await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}static/data/`);
    await window.spire.FetchFileToVFS('ARIALUNI.TTF', "/Library/Fonts/", `${process.env.PUBLIC_URL}static/font/`);

    // Create a Document instance and load the template
    const doc = new docModule.Document();
    doc.LoadFromFile(inputFileName);

    // Field names and field values correspond by index
    let fieldNames = ["Contact Name", "Fax", "Date"];
    let fieldValues = ["John Smith", "+1 (69) 123456", docModule.DateTime.get_Now().Date.ToString()];

    // Execute the mail merge
    doc.MailMerge.Execute(fieldNames, fieldValues);

    // Save the result file
    const outputFileName = 'MailMerge-result.docx';
    doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
    doc.Dispose();

    // 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/vnd.openxmlformats-officedocument.wordprocessingml.document' });
    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>Execute a Mail Merge</h1>
      <button onClick={MailMerge}>Generate</button>
    </div>
  );
}

export default App;

The merge logic itself is a single line, doc.MailMerge.Execute(fieldNames, fieldValues); the rest of the code delivers the template to the VFS and retrieves the result for download. DateTime.get_Now().Date.ToString() returns a string representation of the current date, which can be passed directly as the field value.

After the merge, all five merge field occurrences in the template are replaced with the corresponding field values, and the rest of the layout remains unchanged:

Merge Field Before Merge After Merge
Contact Name «Contact Name» John Smith
Fax «Fax» +1 (69) 123456
Date «Date» 10/22/2026 12:00:00 AM

The three occurrences of Contact Name (recipient row, salutation, closing contact) all become John Smith, which shows that fields with the same name share one value.

Word document after a mail merge has been executed with JavaScript in React

Word document after a mail merge has been executed with JavaScript in React


Format Date Fields by Culture

The output format of DateTime.get_Now().Date.ToString() depends on the culture settings of the runtime environment: a Chinese environment produces 2026/10/22 上午12:00:00, while switching to an English environment may produce 10/22/2026 12:00:00 AM. For a recipient in Germany, the conventional form is 22.10.2026, 17:19:45 — day first, month in the middle, separated by dots.

The mail merge itself only places a piece of text at the field position and does not rearrange the date. The way to control the date format is therefore to format the value as a string for the target culture before it reaches MailMerge.Execute(). The browser's built-in Intl.DateTimeFormat performs this step; its first argument is the locale code and its second argument is the format options.

The specific steps are as follows:

  1. Get the Spire.Doc WASM module and confirm that it is ready.
  2. Load the mail merge template and the font into the virtual file system (VFS).
  3. Use Intl.DateTimeFormat("de-DE", formatOptions) to format the current time as a German-style date string.
  4. Replace the value of the Date field with this string, and keep the other field values unchanged.
  5. Call the Document.MailMerge.Execute() method to execute the mail merge.
  6. Save the result file with the Document.SaveToFile() method, read it back from the VFS, and trigger a browser download.

The code for formatting the date field by culture is as follows:

function App() {
  const ChangeLocale = async () => {
    // Get the Spire.Doc WASM module
    const docModule = window.wasmModule?.spiredoc;

    // Check if the module is ready
    if (!docModule) {
      alert('Spire.Doc is not ready yet');
      return;
    }

    // Load the mail merge template and the font
    let inputFileName = 'MailMerge.docx';
    await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}static/data/`);
    await window.spire.FetchFileToVFS('ARIALUNI.TTF', "/Library/Fonts/", `${process.env.PUBLIC_URL}static/font/`);

    // Create a Document instance and load the template
    const doc = new docModule.Document();
    doc.LoadFromFile(inputFileName);

    // Prepare the date string for the German culture (day.month.year)
    const datestr = new Intl.DateTimeFormat('de-DE', {
      year: 'numeric', month: '2-digit', day: '2-digit',
      hour: '2-digit', minute: '2-digit', second: '2-digit'
    }).format(new Date());

    // Field names and field values correspond by index
    let fieldNames = ["Contact Name", "Fax", "Date"];
    let fieldValues = ["John Smith", "+1 (69) 123456", datestr];

    // Execute the mail merge
    doc.MailMerge.Execute(fieldNames, fieldValues);

    // Save the result file
    const outputFileName = 'ChangeLocale-result.docx';
    doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
    doc.Dispose();

    // 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/vnd.openxmlformats-officedocument.wordprocessingml.document' });
    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>Format Date Fields by Culture</h1>
      <button onClick={ChangeLocale}>Generate</button>
    </div>
  );
}

export default App;

The template is exactly the same and the merge call is exactly the same; what changes is the source of the date string in fieldValues:

const datestr = new Intl.DateTimeFormat('de-DE', { /* format options */ }).format(new Date());

Intl.DateTimeFormat is provided natively by the browser. The locale code can be switched among de-DE, en-US, fr-FR and ja-JP, and the date convention changes accordingly.

With the same template and the same set of data, only the format of the Date field differs:

Source of the date field Merge result
DateTime.get_Now().Date.ToString() 10/22/2026 12:00:00 AM
Intl.DateTimeFormat('de-DE', …).format(new Date()) 22.10.2026, 17:19:45

Word document after the date field has been formatted by culture with JavaScript in React

Word document after the date field has been formatted by culture with JavaScript in React

Output a Localized Date with toLocaleString

In addition to Intl.DateTimeFormat, the Date object itself provides localized output methods. toLocaleString() accepts the same locale code and format options and produces the same result:

const datestr = new Date().toLocaleString('de-DE', {
  year: 'numeric', month: '2-digit', day: '2-digit',
  hour: '2-digit', minute: '2-digit', second: '2-digit'
});

When only the date part is needed, use toLocaleDateString('de-DE', { year: 'numeric', month: '2-digit', day: '2-digit' }) instead. In data exchange scenarios that require an ISO-style form, toISOString().slice(0, 10) returns a string such as 2026-10-22 (this method uses the UTC date).


FAQ

Why does the merged document still contain a placeholder such as «Contact Name»

Cause: The placeholder text belongs to the field's result area, which lies between the separate and end markers of the field code. If « and » were typed as text outside the field, they are not part of that field; MailMerge.Execute() only replaces content inside the field, and text outside it remains as it is.

Solution: In Word, press Ctrl + F9 to create the field, enter the field code, and then press F9 so that Word generates the result text. Do not type the angle brackets manually outside the field. When the template is generated from code, « and » must be written between the separate and end markers, not as a separate run outside the field.

How should a field name that contains a space be written in the template and in JavaScript

In a Word field code, a field name containing spaces or other special characters is enclosed in double quotes:

{ MERGEFIELD "Contact Name" \* MERGEFORMAT }

What is passed to MailMerge.Execute() is the field name itself, without quotes:

let fieldNames = ["Contact Name"];

What is the result when a field in the template is not included in fieldNames

The field's result area is merged as an empty string rather than keeping the original placeholder, and the other fields in the template are processed as usual. This behavior can be used to leave a blank automatically when data is missing; if the original placeholder text should be kept, add the field name to fieldNames and place the original value at the corresponding position in fieldValues.

In addition, field name matching is case-insensitive, so "contact name" and "Contact Name" match the same field. The value actually used is determined by the array index, so the field names and the field values must keep the same order.

Why does the date appear differently on different computers after it is merged into the document

DateTime.get_Now().Date.ToString() produces output according to the current culture settings of the runtime environment, so systems or browsers with different regional settings return different strings. For stable output, follow the approach in "Format Date Fields by Culture": specify the locale code explicitly with Intl.DateTimeFormat and then pass the result to Execute.

In addition, in the environment used for the examples in this article, the time portion returned by DateTime.get_Now() is 12:00:00 AM, meaning that only the date is carried over. When a specific time is required, use the browser's Date object together with Intl.DateTimeFormat to generate the date string.

The downloaded docx will not open and is reported as corrupted

Cause: The Blob's MIME type is incomplete, or the downloaded bytes are not the ones read from the VFS after SaveToFile().

Solution: Declare the full MIME type for the docx and read the file only after saving is complete:

const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], {
  type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
});

Get a Free License

Spire.Doc for JavaScript offers a 30-day full-featured free trial license with no functional limitations. Apply here to evaluate before purchasing.

The charts, flow diagrams, and logos exported on the web are mostly SVG, yet printing, archiving, or an approval workflow usually asks for PDF. Saving them one by one in office software takes a person sitting through it, and batch-converting on a server means uploading the files first.

This article uses Spire.PDF for JavaScript to bridge SVG and PDF. It is built on WebAssembly, loading and saving documents in the browser, and it reads and writes files through a virtual file system (VFS), with no backend involved. There are two paths: turn an SVG into a PDF file on its own, or draw the SVG as vector graphics onto a chosen page of an existing PDF.

This article covers two core features:

For installation and project configuration, refer to Integrate Spire.PDF for JavaScript in a React Project. The examples below assume Spire.PDF is already installed and the WebAssembly module is initialized.


Convert an SVG to PDF

Spire.PDF for JavaScript provides the PdfDocument.LoadFromSvg method, which loads an SVG as a PDF document. The loaded document has a single page, and the page size is taken from the width/height on the SVG root element.

The steps are as follows:

  1. Get the Spire.PDF WASM module and make sure it is ready.
  2. Read the SVG file into the virtual file system (VFS).
  3. Create a PdfDocument object and load the SVG with the LoadFromSvg() method.
  4. Use the PdfDocument.SaveToFile() method to save the result file.
  5. Read the result file from the VFS and trigger a browser download.

The code to convert an SVG to PDF is as follows:

function App() {
  const convertSvgToPdf = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check if the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Load the SVG to convert into the VFS
    const inputFileName = 'VectorChart.svg';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);

    // Load the SVG: the document has a single page, the size of the SVG
    let doc = new pdfModule.PdfDocument();
    doc.LoadFromSvg({ fileName: inputFileName });

    // Save the result file
    const outputFileName = 'VectorChart.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>Convert SVG to PDF</h1>
      <button onClick={convertSvgToPdf}>
        Convert to PDF
      </button>
    </div>
  );
}

export default App;

The result is a single-page PDF whose page size matches the SVG:

The single-page PDF converted from the SVG bar chart


Draw an SVG onto an Existing PDF

Spire.PDF for JavaScript also provides the PdfPageBase.CreateTemplate method and the PdfCanvas.DrawTemplate method, which overlay an SVG onto an already laid-out PDF. Load the SVG, turn its single page into a template, then draw it onto the target page at a chosen position and size — the original document's page count, page size, and existing content are untouched.

The steps are as follows:

  1. Get the Spire.PDF WASM module and make sure it is ready.
  2. Read the existing PDF and the SVG logo into the virtual file system (VFS).
  3. Create a PdfDocument object for each and load the existing PDF and the SVG.
  4. Use the CreateTemplate() method to turn the SVG's first page into a template, then draw it onto the target page with the Canvas.DrawTemplate() method, at a given position and size.
  5. Use the SaveToFile() method to save the PDF that was drawn on, then read the result file from the VFS and trigger a browser download.

The code to draw an SVG onto an existing PDF is as follows:

function App() {
  const drawSvgOnPdf = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check if the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Load both the existing PDF and the SVG logo into the VFS
    const pdfFileName = 'ProductManual.pdf';
    const svgFileName = 'VectorLogo.svg';
    await window.spire.FetchFileToVFS(pdfFileName, '', `${process.env.PUBLIC_URL}/data/`);
    await window.spire.FetchFileToVFS(svgFileName, '', `${process.env.PUBLIC_URL}/data/`);

    // Load the existing PDF
    let existingPdf = new pdfModule.PdfDocument();
    existingPdf.LoadFromFile(pdfFileName);

    // Load the SVG and turn its single page into a template
    let svgDoc = new pdfModule.PdfDocument();
    svgDoc.LoadFromSvg({ fileName: svgFileName });
    const template = svgDoc.Pages.get_Item(0).CreateTemplate();

    // Draw the template on page 1: position and size are in points, origin at the top-left of the page
    const location = new pdfModule.PointF(390, 50);
    const size = new pdfModule.SizeF(130, 60);
    existingPdf.Pages.get_Item(0).Canvas.DrawTemplate({ template, location, size });

    // Save the PDF that was drawn on
    const outputFileName = 'ProductManual_WithLogo.pdf';
    existingPdf.SaveToFile(outputFileName);
    svgDoc.Close();
    existingPdf.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 SVG on Existing PDF</h1>
      <button onClick={drawSvgOnPdf}>
        Insert SVG
      </button>
    </div>
  );
}

export default App;

The original manual content is unchanged, with a vector logo added at the top right:

The SVG logo inserted at the top right of page 1 of the existing PDF


Frequently Asked Questions

Why isn't the output PDF A4-sized

Cause: When LoadFromSvg creates the page it takes the width/height from the SVG root element, converting pixels to points at 75%. In the example, a 760 × 520 SVG becomes a single page of 570 × 390 points — nothing to do with A4.

Solution: To use another size, change the width, height, or viewBox on the SVG root element and convert again.

How do I draw the SVG on another page or at another position

Cause: Which page DrawTemplate draws on is decided by how the page is fetched, and which corner it lands in is decided by location; both are passed in explicitly by the caller.

Solution: Switch pages with Pages.get_Item(index) (0-based), change the position via PointF, and the size via SizeF — all in points, with the origin at the top-left of the page:

// Draw on page 2, bottom left, at a size of 100 × 46 points
existingPdf.Pages.get_Item(1).Canvas.DrawTemplate({
  template: template,
  location: new pdfModule.PointF(40, 760),
  size: new pdfModule.SizeF(100, 46),
});

Is the inserted SVG vector or a bitmap

Cause: What CreateTemplate returns is a PDF template corresponding to the SVG content, and DrawTemplate draws it into the page as vectors, without going through a bitmap.

Solution: Nothing extra to set. Zoom in on the page afterwards and the logo's edges stay crisp; it prints without blurring either.


Get a Free License

If you would like to remove the evaluation message from the result document or lift feature restrictions, please contact sales for a 30-day temporary license.

The same Word template often has to look different on different occasions: in a letter to a customer the name should carry a courtesy title, while in an internal filing table it should be upper case throughout. Editing the template field by field means maintaining a new file for every variation; assembling the judgement in code means the template loses its value for reuse.

This article handles both requirements in the browser with Spire.Doc for JavaScript. One is handled by field switches: instructions such as \b, \f and \* Upper are written into the template's field codes, so the same field value can land in the document in different shapes. The other is handled by a conditional field: the document hard-codes no sentence at all, but carries an IF field that lets Word decide, when the document is saved, which sentence the current data calls for; this kind of field is assembled dynamically in code.

The whole process runs in the browser on WebAssembly and manages the template document through a virtual file system (VFS); it needs neither an Office installation nor a backend service.

This article covers two core features:

For installation and project setup, refer to Integrating Spire.Doc for JavaScript in a React Project. The examples below assume Spire.Doc is installed and the WebAssembly module is initialized.


Use Field Switches in a Merge Field

A field switch is a short instruction written inside the MERGEFIELD field code, after the field name. It does not change the value passed in; it changes only the shape in which the value is written into the document: a prefix, a suffix, a change of case. Because the switch belongs to the template itself, hanging different switches on the same field in different places produces different results within one document.

The merge field switches used most often are:

Switch What it does
(none) Outputs the field value as it is
\b "Mr " Adds a prefix; the argument includes a trailing space
\b Mr Adds a prefix; the argument has no space and sits flush against the field value
\f " (Author)" Adds a suffix; the argument includes a leading space
\* Upper Converts everything to upper case
\* Lower Converts everything to lower case
\* FirstCap Capitalises the first character only
\* Caps Capitalises the first letter of every word
\* MERGEFORMAT Keeps the field formatting; the text is unchanged

Prepare the Template

To lay the output of every switch side by side for comparison, the sample uses a Customer Contact Card as its template: the same merge field XX_Name appears ten times in the “Name Format Comparison” table, each occurrence carrying a different switch, so the merge only has to supply the value once.

The ten field codes in the template are as follows ({ } are Word's field braces, which you type with Ctrl + F9):

{ MERGEFIELD  XX_Name }
{ MERGEFIELD  XX_Name  \b "Mr " }
{ MERGEFIELD  XX_Name  \b Mr }
{ MERGEFIELD  XX_Name  \f " (Author)" }
{ MERGEFIELD  XX_Name  \* Upper }
{ MERGEFIELD  XX_Name  \* Lower }
{ MERGEFIELD  XX_Name  \* FirstCap }
{ MERGEFIELD  XX_Name  \* Caps }
{ MERGEFIELD  XX_Name  \* MERGEFORMAT }
{ MERGEFIELD  XX_Name  \b "Mr " \f " (Author)" }

Spaces separate the field name from the switches and the switches from one another. If the text argument of a switch itself contains a space, wrap it in double quotes (as in \b "Mr "); in that case the space inside the quotes is part of the argument.

The Code

The steps are as follows:

  1. Get the Spire.Doc WASM module and confirm that it is ready.
  2. Load the sample template into the virtual file system (VFS), create a Document object and load the file.
  3. Prepare the field name and field value arrays: the field name is XX_Name and the field value is jason tang.
  4. Call Document.MailMerge.Execute() to run the mail merge.
  5. Save the result with Document.SaveToFile().
  6. Read the result file back from the VFS and trigger the browser download.

The code for using field switches in a Word merge field is as follows:

function App() {
  const MailMergeSwitches = async () => {
    // Get the Spire.Doc WASM module
    const docModule = window.wasmModule?.spiredoc;

    // Check if the module is ready
    if (!docModule) {
      alert('Spire.Doc is not ready yet');
      return;
    }

    // Load the sample template into the virtual file system (VFS)
    let inputFileName = "MailMergeSwitches.docx";
    await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}static/data/`);

    // Create a document and load the template
    const doc = new docModule.Document();
    doc.LoadFromFile(inputFileName);

    // Provide a value for one field only; all ten occurrences in the template use it
    let fieldNames = ["XX_Name"];
    let fieldValues = ["jason tang"];

    // Execute the mail merge
    doc.MailMerge.Execute(fieldNames, fieldValues);

    // Define the output file name
    const outputFileName = "MailMergeSwitches-result.docx";

    // Save the document
    doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });

    doc.Dispose()

    // Read the saved file and convert it to a Blob
    const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([modifiedFileArray], {type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"});
    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>Merge data into a Word template that carries field switches</h1>
      <button onClick={MailMergeSwitches}>Generate</button>
    </div>
  );
}

export default App;

Notice that the code contains no switch-related API at all: every switch lives in the template's field codes, and Execute only has to give XX_Name a value. This is exactly how a field switch is meant to be used — the data stays the same, and the template decides the shape. The sample deliberately passes the value in lower case, as jason tang; if Jack Tang were passed, several of the case-related switches would produce output indistinguishable from the default row.

The Result

Merged with jason tang, the result is as follows:

Word output after the customer contact card has been merged, with all ten XX_Name occurrences generated from a single field value

Word output after the customer contact card has been merged, with all ten XX_Name occurrences generated from a single field value

Field switch What it does Merged result (XX_Name)
(no switch) Outputs the value as is jason tang
\b "Mr " Prefix, trailing space Mr jason tang
\b Mr Prefix, no space Mrjason tang
\f " (Author)" Suffix, leading space jason tang (Author)
\* Upper Converts to upper case JASON TANG
\* Lower Converts to lower case jason tang
\* FirstCap First character only Jason tang
\* Caps Every word capitalised Jason Tang
\* MERGEFORMAT Keeps field formatting jason tang
\b "Mr " \f " (Author)" Prefix and suffix Mr jason tang (Author)

A few points are worth noting:

  • The difference between \b "Mr " and \b Mr is that pair of quotes. The argument of the first is Mr with a trailing space, which outputs Mr jason tang; the argument of the second is Mr with no space, so it sits flush against the field value and becomes Mrjason tang. To leave a gap between the added text and the field value, write the space inside the quotes.
  • \b is a prefix and \f is a suffix, and both can appear in the same field.
  • \* FirstCap capitalises only the first character (Jason tang), while \* Caps capitalises the first letter of every word (Jason Tang). The two names are similar but the effects differ.
  • \* MERGEFORMAT does not affect the text. It preserves the field's formatting, Word adds it by default when it creates a merge field.

Combine a Prefix and a Suffix in One Field

When a position needs both a courtesy title and a signature, there is no need to split \b and \f into two fields. Write both into the field code of the same field, in the order “prefix first, suffix second”; line 10 of the template is written exactly that way:

{ MERGEFIELD  XX_Name  \b "Mr " \f " (Author)" }

Passing jason tang produces Mr jason tang (Author) after the merge: one field value, with both the title and the signature attached at once.


Build and Execute a Conditional Field

Field switches can only change the shape; they cannot choose different text according to the data. When “show this sentence if the data meets the condition, otherwise show that one” is needed, a conditional field (an IF field) is required: an IF field holds a merge field as the test, followed by two result texts, and Word evaluates it when the document is saved.

{ IF { MERGEFIELD Count } > "1" "Greater than one" "Less than one" }

Taken apart, this stretch contains four kinds of element:

  1. an IfField, whose field type is set to FieldType.FieldIf;
  2. a merge field node (FieldType.FieldMergeField) — the value being compared in the condition;
  3. the operator and the two branch texts, appended as ordinary text fragments;
  4. a FieldMark of type FieldMarkType.FieldEnd, which closes the field off and has to be assigned back to IfField.End for the field to be complete.

This document has no input template at all; both IF fields are built from scratch, so LoadFromFile is not needed — but the font still has to be loaded first for the layout Word performs when the document is saved.

The Code

The steps are as follows:

  1. Get the Spire.Doc WASM module and confirm that it is ready.
  2. Load the ARIALUNI.TTF font into the virtual file system (VFS).
  3. Create a Document object, add a section and then add the first paragraph.
  4. Build an IF field with IfField, appending in order the merge field that serves as the test, the operator, the comparison basis and the two branch texts.
  5. Create a FieldEnd mark with CreateParagraphItem(), append it to the paragraph and assign it to IfField.End.
  6. Build the second conditional field in the same way.
  7. Call MailMerge.Execute() to supply values for the merge fields inside the conditional fields, and set IsUpdateFields = true.
  8. Save the result file, read it back from the VFS and trigger the browser download.

The code for building and executing a conditional field is as follows:

function App() {
  const ExecuteConditionalField = async () => {
    // Get the Spire.Doc WASM module
    const docModule = window.wasmModule?.spiredoc;

    // Check if the module is ready
    if (!docModule) {
      alert('Spire.Doc is not ready yet');
      return;
    }

    // Load the ARIALUNI.TTF font into the virtual file system (VFS)
    await window.spire.FetchFileToVFS('ARIALUNI.TTF','/Library/Fonts/',`${process.env.PUBLIC_URL}/static/font/`);

    // Create a document and add a section
    let doc = new docModule.Document();
    let section = doc.AddSection();

    // First conditional field: Count > 1
    let paragraph = section.AddParagraph();
    let ifField1 = new docModule.IfField(doc);
    ifField1.Type = docModule.FieldType.FieldIf;
    ifField1.Code = "IF ";
    paragraph.Items.Add(ifField1);
    paragraph.AppendField("Count", docModule.FieldType.FieldMergeField);
    paragraph.AppendText(" > ");
    paragraph.AppendText("\"1\" ");
    paragraph.AppendText("\"Greater than one\" ");
    paragraph.AppendText("\"Less than one\"");
    let end1 = doc.CreateParagraphItem(docModule.ParagraphItemType.FieldMark);
    end1.Type = docModule.FieldMarkType.FieldEnd;
    paragraph.Items.Add(end1);
    ifField1.End = end1;

    // Second conditional field: Age > 50
    paragraph = section.AddParagraph();
    let ifField2 = new docModule.IfField(doc);
    ifField2.Type = docModule.FieldType.FieldIf;
    ifField2.Code = "IF ";
    paragraph.Items.Add(ifField2);
    paragraph.AppendField("Age", docModule.FieldType.FieldMergeField);
    paragraph.AppendText(" > ");
    paragraph.AppendText("\"50\" ");
    paragraph.AppendText("\"The old man\" ");
    paragraph.AppendText("\"The young man\"");
    let end2 = doc.CreateParagraphItem(docModule.ParagraphItemType.FieldMark);
    end2.Type = docModule.FieldMarkType.FieldEnd;
    paragraph.Items.Add(end2);
    ifField2.End = end2;

    // Supply values for the merge fields inside the conditional fields
    let fieldNames = ["Count", "Age"];
    let fieldValues = ["2", "30"];
    doc.MailMerge.Execute(fieldNames, fieldValues);

    // Make the fields evaluate when the document is saved
    doc.IsUpdateFields = true;

    // Define the output file name
    const outputFileName = "ExecuteConditionalField-result.docx";

    // Save the document
    doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });

    doc.Dispose()

    // Read the saved file and convert it to a Blob
    const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([modifiedFileArray], {type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"});
    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>Create and execute a conditional field</h1>
      <button onClick={ExecuteConditionalField}>Generate</button>
    </div>
  );
}

export default App;

A few details of the assembly order matter:

  1. ifField.Code = "IF " only marks the field type. What actually assembles the body of the field is the AppendField and the AppendText calls that follow; they are laid out into this field one after another.
  2. The operator and the two branch texts are all appended with AppendText, and the quotes among them have to be written into the string itself ("\"1\" "). An IF field requires both result texts to be quoted, and these quote characters are what does it.
  3. The order is fixed: compared value → operator → comparison basis → true branch → false branch. The false branch needs no extra space after its closing quote.
  4. FieldEnd is the field's closing mark. It has to be created with CreateParagraphItem(ParagraphItemType.FieldMark), given its end.Type, appended to the paragraph and then assigned to ifField.End. Skip any one of those steps and the field is incomplete.
  5. IsUpdateFields = true decides whether the field is evaluated when the document is saved; it belongs after Execute and before SaveToFile.

The Result

With Count = "2" and Age = "30", the two conditional fields take the true branch and the false branch respectively. The evaluated result of a conditional field is only one line of text and the rest of the page is blank, so the two branches are shown here in a table:

Field values First paragraph (IF Count > "1") Second paragraph (IF Age > "50")
Count = "2", Age = "30" Greater than one The young man
Count = "0", Age = "70" Less than one The old man

Replacing the values flips the result, without changing either the template structure or the code.


FAQ

The conditional field saves as two blank paragraphs — why

Cause: A field in the document is only an instruction waiting to be evaluated. When IsUpdateFields is not set, saving does not trigger evaluation, neither branch's text is output, and the result looks like two blank paragraphs.

Solution: Turn the switch on after Execute and before SaveToFile:

doc.MailMerge.Execute(fieldNames, fieldValues);
doc.IsUpdateFields = true;   // Make the fields evaluate when the document is saved
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });

The prefix added by \b is stuck to the name — what now

Cause: The text argument of a switch carries no space of its own. The argument of \b Mr is the two characters Mr, and the field value follows immediately in the output, which gives Mrjason tang.

Solution: Write the space into the argument and wrap it in double quotes:

{ MERGEFIELD  XX_Name  \b "Mr " }

\f works the same way: write the leading space inside the quotes. One space between the field name and the switch, and between switches, is enough; extra spaces do not affect parsing.

What is the difference between \* FirstCap and \* Caps

\* FirstCap converts only the first character to upper case and leaves the rest alone: jason tang → Jason tang.

\* Caps converts the first letter of every word to upper case: jason tang → Jason Tang.

When checking these two switches, the field value has to be lower case or mixed case; if the value is already Jack Tang, both lines look the same as the default line.

Why do some switches written on a merge field have no effect

Not every Word field switch takes part in the text replacement of a mail merge. \* MERGEFORMAT preserves the field's formatting and never changes the text to begin with; switches such as \m, \v and \# 0.00 (a number format) usually produce no visible change when used on a plain-text field value.

The ones verified in this article and usable as they are: \b, \f, \* Upper, \* Lower, \* FirstCap, \* Caps, and \* MERGEFORMAT, which can be stacked with a prefix or a suffix.

Changing the values of Count and Age does not change the output

Cause: The result text of a conditional field is cached in the document. When IsUpdateFields is not on, saving keeps the previous evaluation; changing only the values passed to Execute without running the merge and saving again naturally leaves the old content in the file.

Solution: After every change, go through “pass values → evaluate → save” again:

// Pass the new Count and Age
doc.MailMerge.Execute(fieldNames, fieldValues);

// Re-evaluate on save so the result follows the new values
doc.IsUpdateFields = true;

doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });

Field name matching is case-insensitive: AppendField("Count", …) and Execute(["count"], …) are treated as the same field. What does affect the result is that the name itself cannot change — a field name not listed in Execute is not replaced, and its result area is left empty.


Get a Free License

Spire.Doc for JavaScript offers a 30-day full-featured free trial license with no functional limitations. Apply here to evaluate before purchasing.

Page 6 of 371
page 6