{
  "$schema": "https://ai.syncfusion.com/schema/registry-item.json",
  "name": "docx-editor-mail-merge",
  "framework": "react",
  "type": "registry:component",
  "component": "DocxEditor",
  "variant": "mail-merge",
  "dependencies": [
    "@syncfusion/ej2-react-documenteditor",
    "@syncfusion/ej2-react-popups",
    "@syncfusion/ej2-react-navigations"
  ],
  "description": "Word document editor with mail-merge fields, insert-field dialog, and merge-document toolbar.",
  "meta": {
    "essentialStudioRelease": "2026 Volume 2 (v34.1.29)",
    "license": "commercial-or-community",
    "licenseNote": "The Syncfusion package is licensed. The composition in this file is source you own and edit. See https://ai.syncfusion.com/licensing.md",
    "requiresLicenseKey": true,
    "requiresServerEndpoint": true,
    "platformIndex": "https://ai.syncfusion.com/react/llms.txt",
    "skillPack": "syncfusion/react-ui-components-skills",
    "docs": "https://ai.syncfusion.com/gallery/react/ts/docx-editor-mail-merge.md"
  },
  "files": [
    {
      "target": "src/components/docx-editor-mail-merge/DocxEditor.tsx",
      "content": "import { useEffect, useRef } from 'react';\nimport { DocumentEditorContainerComponent, Toolbar } from '@syncfusion/ej2-react-documenteditor';\nimport { mailmerge } from './datasource';\nimport { DialogUtility, Dialog } from '@syncfusion/ej2-react-popups';\nimport type { ClickEventArgs } from '@syncfusion/ej2-react-navigations';\nDocumentEditorContainerComponent.Inject(Toolbar);\n\nconst MailMerge = () => {\n     useEffect(() => {\n        rendereComplete();\n    }, []);\n    let hostUrl: string =\n        \"https://document.syncfusion.com/web-services/docx-editor/api/documenteditor/\";\n    let container = useRef<DocumentEditorContainerComponent>(null);\n    const onWrapText = (text: string): string => {\n        let content: string = \"\";\n        let index: number = text.lastIndexOf(\" \");\n        content = text.slice(0, index);\n        text.slice(index);\n        content += '<div class=\"e-de-text-wrap\">' + text.slice(index) + \"</div>\";\n        return content;\n    };\n    let toolbarOptions: object = [\n        \"New\",\n        \"Open\",\n        \"Separator\",\n        \"Undo\",\n        \"Redo\",\n        \"Separator\",\n        {\n            prefixIcon: \"sf-icon-InsertMergeField\",\n            tooltipText: \"Insert Field\",\n            text: onWrapText(\"Insert Field\"),\n            id: \"InsertField\",\n        },\n        {\n            prefixIcon: \"sf-icon-FinishMerge\",\n            tooltipText: \"Merge Document\",\n            text: onWrapText(\"Merge Document\"),\n            id: \"MergeDocument\",\n        },\n        \"Separator\",\n        \"Image\",\n        \"Table\",\n        \"Hyperlink\",\n        \"Bookmark\",\n        \"TableOfContents\",\n        \"Separator\",\n        \"Header\",\n        \"Footer\",\n        \"PageSetup\",\n        \"PageNumber\",\n        \"Break\",\n        \"Separator\",\n        \"Find\",\n        \"Separator\",\n        \"Comments\",\n        \"TrackChanges\",\n        \"Separator\",\n        \"LocalClipboard\",\n        \"RestrictEditing\",\n        \"Separator\",\n        \"FormFields\",\n        \"UpdateFields\",\n    ];\n    const closeFieldDialog = (): void => {\n        insertFieldDialogObj.hide();\n        container.current.documentEditor.focusIn();\n    };\n    let insertFieldDialogObj: Dialog = new Dialog({\n        header: \"Merge Field\",\n        content:\n            '<div class=\"dialogContent\">' +\n            // tslint:disable-next-line:max-line-length\n            '<label class=\"e-insert-field-label\">Name:</label></br><input type=\"text\" id=\"field_text\" class=\"e-input\" placeholder=\"Type a field to insert eg. FirstName\">' +\n            \"</div>\",\n        showCloseIcon: true,\n        isModal: true,\n        width: \"auto\",\n        height: \"auto\",\n        close: closeFieldDialog,\n        buttons: [\n            {\n                click: (): void => {\n                    let fieldNameTextBox: any = document.getElementById(\"field_text\");\n                    let fieldName: any = fieldNameTextBox.value;\n                    if (fieldName !== \"\") {\n                        container.current.documentEditor.editor.insertField(\n                            \"MERGEFIELD \" + fieldName + \" \\\\* MERGEFORMAT\"\n                        );\n                    }\n                    insertFieldDialogObj.hide();\n                    container.current.documentEditor.focusIn();\n                },\n                buttonModel: {\n                    content: \"Ok\",\n                    cssClass: \"e-flat\",\n                    isPrimary: true,\n                },\n            },\n            {\n                click: (): void => {\n                    insertFieldDialogObj.hide();\n                    container.current.documentEditor.focusIn();\n                },\n                buttonModel: {\n                    content: \"Cancel\",\n                    cssClass: \"e-flat\",\n                },\n            },\n        ],\n    });\n    const onLoadDefault = (): void => {\n        container.current.documentEditor.open(JSON.stringify(mailmerge));\n        container.current.documentEditor.documentName = \"Mail Merge\";\n        container.current.documentEditorSettings.showRuler = true;\n        let item: any = toolbarOptions;\n        container.current.toolbarItems = item;\n        container.current.documentChange = (): void => {\n            container.current.documentEditor.focusIn();\n        };\n        container.current.toolbarClick = (args: ClickEventArgs): void => {\n            switch (args.item.id) {\n                case \"MergeDocument\":\n                    mergeDocument();\n                    break;\n                case \"InsertField\":\n                    showInsertFielddialog(container);\n                    break;\n            }\n        };\n    };\n    const mergeDocument = (): void => {\n        container.current.documentEditor.saveAsBlob(\"Docx\").then((blob: Blob) => {\n            let exportedDocumment: Blob = blob;\n            let fileReader: any = new FileReader();\n            fileReader.onload = (): void => {\n                let base64String: any = fileReader.result;\n                let responseData: any = {\n                    fileName: container.current.documentEditor.documentName + \".docx\",\n                    documentData: base64String,\n                };\n                // let waitingPopUp:HTMLElement = document.getElementById('waiting-popup');\n                // let inActiveDiv:HTMLElement = document.getElementById('popup-overlay');\n                showHideWaitingIndicator(true);\n                let baseUrl: string =\n                    \"https://document.syncfusion.com/web-services/docx-editor/api/documenteditor/MailMerge\";\n                let httpRequest: XMLHttpRequest = new XMLHttpRequest();\n                httpRequest.open(\"POST\", baseUrl, true);\n                httpRequest.setRequestHeader(\n                    \"Content-Type\",\n                    \"application/json;charset=UTF-8\"\n                );\n                httpRequest.onreadystatechange = () => {\n                    if (httpRequest.readyState === 4) {\n                        if (httpRequest.status === 200 || httpRequest.status === 304) {\n                            container.current.documentEditor.open(httpRequest.responseText);\n                        } else {\n                            // Failed to merge document\n                            DialogUtility.alert({\n                                title: \"Information\",\n                                content: \"failure to merge document\",\n                                showCloseIcon: true,\n                                closeOnEscape: true,\n                            });\n                        }\n                        showHideWaitingIndicator(false);\n                    }\n                };\n                httpRequest.send(JSON.stringify(responseData));\n            };\n            fileReader.readAsDataURL(blob);\n        });\n    };\n    const showHideWaitingIndicator = (show: boolean) => {\n        let waitingPopUp: HTMLElement = document.getElementById(\"waiting-popup\");\n        let inActiveDiv: HTMLElement = document.getElementById(\"popup-overlay\");\n        inActiveDiv.style.display = show ? \"block\" : \"none\";\n        waitingPopUp.style.display = show ? \"block\" : \"none\";\n    };\n    const showInsertFielddialog = (container): void => {\n        let instance: any = this;\n        if (\n            document.getElementById(\"insert_merge_field\") === null ||\n            document.getElementById(\"insert_merge_field\") === undefined\n        ) {\n            let fieldcontainer: any = document.createElement(\"div\");\n            fieldcontainer.id = \"insert_merge_field\";\n            document.body.appendChild(fieldcontainer);\n            insertFieldDialogObj.appendTo(\"#insert_merge_field\");\n            fieldcontainer.parentElement.style.position = \"fixed\";\n            fieldcontainer.style.width = \"auto\";\n            fieldcontainer.style.height = \"auto\";\n        }\n        insertFieldDialogObj.close = (): void => {\n            container.documentEditor.focusIn();\n        };\n        insertFieldDialogObj.beforeOpen = (): void => {\n            container.documentEditor.focusIn();\n        };\n        insertFieldDialogObj.show();\n        let fieldNameTextBox: any = document.getElementById(\"field_text\");\n        fieldNameTextBox.value = \"\";\n    };\n    const rendereComplete = (): void => {\n        container.current.documentEditor.pageOutline = \"#E0E0E0\";\n        container.current.documentEditor.acceptTab = true;\n        container.current.documentEditor.resize();\n        onLoadDefault();\n    };\n    return (\n        <div className=\"control-pane\">\n            <div className=\"control-section\">\n                <div\n                    className=\"control-section\"\n                    style={{ paddingLeft: \"0px\", paddingRight: \"0px\", paddingTop: \"0px\" }}\n                >\n                    <DocumentEditorContainerComponent\n                        id=\"container\"\n                        ref={container}\n                        style={{ display: \"block\" }}\n                        height={\"590px\"}\n                        serviceUrl={hostUrl}\n                        enableToolbar={true}\n                        locale=\"en-US\"\n                    />\n                </div>\n            </div>\n\n            <div className=\"overlay\" id=\"popup-overlay\"></div>\n            <div id=\"waiting-popup\">\n                <svg className=\"circular\" height=\"40\" width=\"40\">\n                    <circle\n                        className=\"circle-path\"\n                        cx=\"25\"\n                        cy=\"25\"\n                        r=\"20\"\n                        fill=\"none\"\n                        strokeWidth=\"6\"\n                        strokeMiterlimit=\"10\"\n                    />\n                </svg>\n            </div>\n        </div>\n    );\n}\nexport default MailMerge;"
    },
    {
      "target": "src/components/docx-editor-mail-merge/datasource.ts",
      "content": "export let mailmerge = { \"sfdt\": \"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\" };\n"
    }
  ]
}