Skip to content

Fill Form Fields and Save with FormData - React

In this example we allow the user to complete a pdf with fields and save the completed document.

Prerequisites

This example assumes that there exists a document form.pdf in your storage. If you do not have such a document you can run the following command to add a document with fields under that name:

shell
node src/post-document.js

Field handling

You can use the loaded and formdatachanged events on the pdf document element to capture those events.

html
}
<PdfDocument
  ref={pdfDocument}
  onLoaded={onLoaded}
  onFormdatachanged={onFormDataChanged}
/>
{

Inside these event handlers you can retrieve the field values:

tsx
const onLoaded = useCallback(
  async ({ target }: PdfDocumentCustomEvent<string | null>) => {
    setFormData(await target.getFormData());
  },
  [],
);
tsx
const onFormDataChanged = async ({
  target,
}: PdfDocumentCustomEvent<unknown>) => {
  setFormData(await target.getFormData());
};

Saving documents

You can save the pdf document with the filled in fields to your storage.

note

UnoPdf nevers mutates documents in it's storage. It always creates a copy of your document.

Create a save button with an click event handler.

html
*/}
<button
  type="button"
  style={{ fontSize: "20px" }}
  onClick={saveDocument}
>
  save
</button>
{/*

In the click event handler you can call the save method on the document element. In addition you can get the field value from the document and post those to an application endpoint to start or continue the business workflow of which the completion of this form is a part.

ts
const saveDocument = async () => {
  if (pdfDocument.current) {
    const responseDocument = await pdfDocument.current.save("response.pdf")
    if (responseDocument) {
      console.log(`document saved with id ${responseDocument.id}`);
    } else {
      console.log(`document not saved`);
    }
    setResponseDocument(responseDocument);
    // In addition to saving the pdf, you can also access
    // the form data at this point to submit it
    // e.g. to a rest endpoint for processing.
    // The pdf that is saved can be kept as part
    // of the audit trail for the transaction.
    console.log("form data", await pdfDocument.current.getFormData());
  }
};

Screenshot

Screenshot op fill-save

Code

tsx
import {
  IPdfDocument,
  PdfApplication,
  PdfDocument,
  PdfDocumentCustomEvent,
  PdfPages,
  defineCustomElements,
} from "@tallcomponents/unopdf-react";

import { Fragment, useCallback, useEffect, useRef, useState } from "react";

defineCustomElements();

// Note: this example uses Vite and retrieves the keys from the environment.
// This should not be used in production.
// See the authentication section in the usage guide for more information.

const publickey = import.meta.env.VITE_PUBLIC_KEY;
const privatekey = import.meta.env.VITE_PRIVATE_KEY;

function App() {
  const [formDocument, setFormDocument] = useState<IPdfDocument>();
  const pdfApplication = useRef<HTMLPdfApplicationElement | null>(null);
  const pdfDocument = useRef<HTMLPdfDocumentElement | null>(null);

  const [formData, setFormData] = useState<Record<string, string>>({});
  const [responseDocument, setResponseDocument] =
    useState<IPdfDocument | null>();

  useEffect(() => {
    function getFormDocument() {
      if (pdfApplication.current) {
        pdfApplication.current.getDocuments().then((documents) => {
          const formDocument = documents.find(
            (doc) => doc.originalFileName == "form.pdf",
          );
          if (formDocument) {
            setFormDocument(formDocument);
          }
        });
      }
    }
    getFormDocument();
  }, [pdfApplication]);

  useEffect(() => {
    if (pdfDocument.current && formDocument && formDocument.id) {
      pdfDocument.current.open(formDocument.id);
    }
  }, [pdfDocument, formDocument]);

  // #region set-form-data-on-load
  const onLoaded = useCallback(
    async ({ target }: PdfDocumentCustomEvent<string | null>) => {
      setFormData(await target.getFormData());
    },
    [],
  );
  // #endregion set-form-data-on-load

  // #region set-form-data-on-change
  const onFormDataChanged = async ({
    target,
  }: PdfDocumentCustomEvent<unknown>) => {
    setFormData(await target.getFormData());
  };
  // #endregion set-form-data-on-change

  // #region save-document-handler
  const saveDocument = async () => {
    if (pdfDocument.current) {
      const responseDocument = await pdfDocument.current.save("response.pdf")
      if (responseDocument) {
        console.log(`document saved with id ${responseDocument.id}`);
      } else {
        console.log(`document not saved`);
      }
      setResponseDocument(responseDocument);
      // In addition to saving the pdf, you can also access
      // the form data at this point to submit it
      // e.g. to a rest endpoint for processing.
      // The pdf that is saved can be kept as part
      // of the audit trail for the transaction.
      console.log("form data", await pdfDocument.current.getFormData());
    }
  };
  // #endregion save-document-handler

  const downloadDocument = async () => {
    await pdfDocument.current?.download();
  };

  return (
    <>
      <h1>UnoPdf Example: Fill Form Fields and Save with FormData in React</h1>

      <PdfApplication
        ref={pdfApplication}
        publickey={publickey}
        privatekey={privatekey}
      />

      {
        // #region pdf-document
      }
      <PdfDocument
        ref={pdfDocument}
        onLoaded={onLoaded}
        onFormdatachanged={onFormDataChanged}
      />
      {
        // #endregion pdf-document
      }

      <div style={{ display: "flex" }}>
        <div>
          <PdfPages />
        </div>
        <div>
          <div style={{ padding: "10px" }}>
            You can fill out the fields in the document and see your changes
            reflected below
          </div>
          <div style={{ padding: "10px" }}>
            <dl>
              {Object.entries(formData).map(([name, value]) => (
                <Fragment key={name}>
                  <dt>{name}:</dt>
                  <dd style={{ fontWeight: "bold", paddingBottom: "10px" }}>
                    {value || "\u00A0"}
                  </dd>
                </Fragment>
              ))}
            </dl>
          </div>
          <div style={{ display: "flex" }}>
            <div style={{ padding: "10px" }}>
              {/*
              <!-- #region save-document-button -->
              */}
              <button
                type="button"
                style={{ fontSize: "20px" }}
                onClick={saveDocument}
              >
                save
              </button>
              {/*
              <!-- #endregion save-document-button -->
              */}
            </div>
            <div style={{ padding: "10px" }}>
              <button
                type="button"
                style={{ fontSize: "20px" }}
                onClick={downloadDocument}
              >
                download
              </button>
            </div>
            {responseDocument && (
              <div style={{ padding: "10px" }}>
                Document saved as {JSON.stringify(responseDocument)}
              </div>
            )}
          </div>
        </div>
      </div>
    </>
  );
}

export default App;

Running the example

  • Download the react - Fill Form Fields and Save with FormData project
  • Unzip the file to a directory react-fill-save.
    shell
    unzip react-fill-save.zip -d react-fill-save
  • Open a terminal and go to that directory
    shell
    cd react-fill-save
  • Install dependencies
    shell
    npm install
    shell
    yarn
  • Start the project
    shell
    npm run start
    shell
    yarn start