# Fill a DOCX template

> Update every content-control occurrence that represents an application field, then export the DOCX.



Use a prepared template when your application already knows the values to put in a document. This agreement has a
client name in three places and an auto-renew checkbox. The fields are already present; your application fills them.

## Try the workflow [#try-the-workflow]

Expand the Editor and change the client name. All three occurrences update. Then toggle auto-renew to change the
checkbox in the document.

> **Interactive editor: Fill the template**
>
> The application form updates real Word content controls in the service-agreement DOCX.
>
> - Client legal name (`client.legalName`): one form value updates 3 document occurrences.
> - Auto-renew (`agreement.autoRenew`): the checkbox updates the Word checkbox control.
>
> The demo reports how many matching controls changed. Reset restores the prepared template, and Export DOCX downloads the filled document.


## Start with the prepared example [#start-with-the-prepared-example]

Open the [complete Vanilla TypeScript example](https://go.superdoc.dev/examples/content-controls?workflow=fill) and
choose **Fill fields**. It includes the template, form, occurrence arrows, Editor setup, and export button. The snippets below are from
that project; they are not a separate Editor setup.

In your own integration, keep the Editor from the [Quickstart](/editor/quickstart) and load a DOCX with content controls,
such as the [service-agreement template](/fixtures/service-agreement-template.docx). Obtain `superdoc.activeEditor.doc`
after `onReady` before calling the field helpers. The Quickstart's original sample does not contain these fields.

## Define the field map [#define-the-field-map]

The client-name controls share the tag `client.legalName`. Their IDs differ because each ID identifies one occurrence.
Changing a control does not automatically change others with the same tag; the example updates every match.

Keep the tag and expected type together. For example, `client.legalName` expects a text control and
`agreement.autoRenew` expects a checkbox.

<details>
  <summary>
    View the example's field map
  </summary>

  ```ts
  export const templateFields = [
    {
      key: 'clientLegalName',
      label: 'Client legal name',
      tag: 'client.legalName',
      type: 'text',
    },
    {
      key: 'clientAddress',
      label: 'Client address',
      tag: 'client.address',
      type: 'text',
    },
    {
      key: 'effectiveDate',
      label: 'Effective date',
      tag: 'agreement.effectiveDate',
      type: 'text',
    },
    {
      key: 'autoRenew',
      label: 'Auto-renew',
      tag: 'agreement.autoRenew',
      type: 'checkbox',
    },
  ] as const;

  export type TemplateField = (typeof templateFields)[number];
  export type TemplateFieldKey = TemplateField['key'];

  type TaggedContentControl = {
    readonly controlType: string;
    readonly properties: { readonly tag?: string };
  };

  export function hasCompatibleTemplateFields(items: readonly TaggedContentControl[]) {
    return templateFields.every((field) =>
      items.some((item) => item.properties.tag === field.tag && item.controlType === field.type),
    );
  }

  ```
</details>

## Update every occurrence [#update-every-occurrence]

The example's `updateTextField()` and `updateCheckboxField()` helpers follow three steps:

1. Find matching controls with `selectByTag()`.
2. Check each control's type, then call `text.setValue()` or `checkbox.setState()` on its target.
3. Count updated, unchanged, and failed occurrences from the mutation receipts.

<details>
  <summary>
    View the typed update helpers
  </summary>

  ```ts
  import type { BrowserDocumentApi, ContentControlInfo } from 'superdoc/ui';

  export type FieldUpdateResult = {
    failures: string[];
    matched: number;
    unchanged: number;
    updated: number;
  };

  type ControlType = 'checkbox' | 'text';

  async function updateControls(
    doc: BrowserDocumentApi,
    tag: string,
    expectedType: ControlType,
    mutate: (control: ContentControlInfo) => Promise<{ success: boolean; failure?: { code?: string; message?: string } }>,
  ): Promise<FieldUpdateResult> {
    let items: readonly ContentControlInfo[];
    try {
      ({ items } = await doc.contentControls.selectByTag({ tag }));
    } catch (error) {
      return {
        failures: [error instanceof Error ? error.message : `Could not find controls for ${tag}.`],
        matched: 0,
        unchanged: 0,
        updated: 0,
      };
    }
    const result: FieldUpdateResult = { failures: [], matched: items.length, unchanged: 0, updated: 0 };

    for (const control of items) {
      if (control.controlType !== expectedType) {
        result.failures.push(`${control.id} is ${control.controlType}, not ${expectedType}.`);
        continue;
      }

      try {
        const receipt = await mutate(control);
        if (receipt.success) result.updated += 1;
        else if (receipt.failure?.code === 'NO_OP') result.unchanged += 1;
        else result.failures.push(receipt.failure?.message ?? `Could not update ${control.id}.`);
      } catch (error) {
        result.failures.push(error instanceof Error ? error.message : `Could not update ${control.id}.`);
      }
    }

    return result;
  }

  export function updateTextField(doc: BrowserDocumentApi, tag: string, value: string) {
    return updateControls(doc, tag, 'text', (control) =>
      Promise.resolve(doc.contentControls.text.setValue({ target: control.target, value })),
    );
  }

  export function updateCheckboxField(doc: BrowserDocumentApi, tag: string, checked: boolean) {
    return updateControls(doc, tag, 'checkbox', (control) =>
      Promise.resolve(doc.contentControls.checkbox.setState({ target: control.target, checked })),
    );
  }

  export function didUpdateEveryMatch(result: { failures: readonly string[]; matched: number }) {
    return result.matched > 0 && result.failures.length === 0;
  }

  export function describeUpdate(result: FieldUpdateResult) {
    if (result.failures.length > 0 && result.matched === 0) return 'The document could not be updated.';
    if (result.matched === 0) return 'No matching controls.';
    if (result.failures.length > 0) return `Updated ${result.updated} of ${result.matched} locations.`;
    if (result.updated === 0) return `${result.matched} locations already match.`;
    return `Updated ${result.updated} ${result.updated === 1 ? 'location' : 'locations'}.`;
  }

  ```
</details>

Use the same lookup and receipt pattern for other supported field types. Check `controlType`, call the matching typed
operation, and use the returned receipt to decide what the application reports.

No matches means the template is missing the expected field. A locked or incompatible occurrence can fail after other
occurrences have changed: these individual updates are not one atomic transaction. The example reports partial results
and blocks export until failed fields are corrected.

## Export and check the result [#export-and-check-the-result]

In the standalone example, choose **Export DOCX** after the updates finish. Reopen the file in SuperDoc or Word and
check all three client names and the checkbox. Field tags remain in the DOCX for later updates.

The embedded demo does not save to your server. Use [Load and save](/editor/load-and-save-documents) to persist the
exported bytes in your application.

Next, [add fields to your own template](/editor/content-controls/add-fields-to-a-docx-template). For an application-owned
field list, see [custom content-control UI](/editor/custom-ui/content-controls).
