Back to all examples

Show a user progress

const sendApi = (
    progressCallback: (progress: number) => void,
    isCancelledRef: { current: boolean }
  ) => {
    return new Promise((resolve, reject) => {
      let progress = 0;
      const interval = setInterval(() => {
        if (isCancelledRef.current) {
          clearInterval(interval);
          reject("cancelled");
          return;
        }

        progress += 5;
        progressCallback(progress);

        if (progress >= 100) {
          clearInterval(interval);
          resolve("success");
        }
      }, 200);
    });
  };

  const downloadReport = () => {
    const isCancelledRef = { current: false };

    const uuid = TemporaryNotification.show("Downloading report D-23459", {
      type: "progress",
      actionText: "Cancel",
      action: () => {
        isCancelledRef.current = true;
        TemporaryNotification.dismiss(uuid);
        console.log("Download cancelled");
      },
    });

    TemporaryNotification.setProgress(uuid, 0);

    const updateProgress = (progress: number) => {
      TemporaryNotification.setProgress(uuid, progress);

      if (progress >= 100) {
        setTimeout(() => {
          TemporaryNotification.show("Report downloaded", {
            type: "success",
            duration: "medium",
            actionText: "View",
            action: () => {
              console.log("View report clicked!");
            },
            cancelUUID: uuid,
          });
        }, 300);
      }
    };

    sendApi(updateProgress, isCancelledRef).catch((error) => {
      if (error !== "cancelled") {
        TemporaryNotification.dismiss(uuid);
      }
    });
  };
<GoabTemporaryNotificationCtrl />
      <GoabxButton type="tertiary" leadingIcon="download" onClick={downloadReport}>
        Download report
      </GoabxButton>
async downloadReportAPI(notificationUuid: string): Promise<Error | undefined> {
    // Perform your API call here with progress tracking
    // Update progress as download progresses (0-100)
    TemporaryNotification.setProgress(notificationUuid, 25);
    // ... continue API work ...
    TemporaryNotification.setProgress(notificationUuid, 50);
    // ... continue API work ...
    TemporaryNotification.setProgress(notificationUuid, 75);
    // ... complete API work ...
    TemporaryNotification.setProgress(notificationUuid, 100);
    return undefined;
  }

  async downloadReport(): Promise<void> {
    const uuid = TemporaryNotification.show("Downloading report D-23459", {
      type: "progress",
      actionText: "Cancel",
      action: () => {
        TemporaryNotification.dismiss(uuid);
      },
    });

    const err = await this.downloadReportAPI(uuid);

    if (err) {
      TemporaryNotification.show("Download failed", {
        type: "error",
        duration: "medium",
        cancelUUID: uuid,
      });
    } else {
      TemporaryNotification.show("Report downloaded", {
        type: "success",
        duration: "medium",
        actionText: "View",
        action: () => {
          console.log("View report clicked!");
        },
        cancelUUID: uuid,
      });
    }
  }
<goab-temporary-notification-ctrl></goab-temporary-notification-ctrl>

<goabx-button type="tertiary" leadingIcon="download" (onClick)="downloadReport()">
  Download report
</goabx-button>
const downloadBtn = document.getElementById("download-btn");
let currentUuid = null;

function showNotification(message, opts = {}) {
  const uuid = crypto.randomUUID();
  document.body.dispatchEvent(new CustomEvent("msg", {
    composed: true,
    bubbles: true,
    detail: {
      action: "goa:temp-notification",
      data: { message, uuid, type: "basic", ...opts }
    }
  }));
  return uuid;
}

function dismissNotification(uuid) {
  document.body.dispatchEvent(new CustomEvent("msg", {
    composed: true,
    bubbles: true,
    detail: {
      action: "goa:temp-notification:dismiss",
      data: uuid
    }
  }));
}

function setProgress(uuid, progress) {
  document.body.dispatchEvent(new CustomEvent("msg", {
    composed: true,
    bubbles: true,
    detail: {
      action: "goa:temp-notification:progress",
      data: { uuid, progress }
    }
  }));
}

async function downloadReportAPI(notificationUuid) {
  setProgress(notificationUuid, 25);
  await new Promise((resolve) => setTimeout(resolve, 500));
  setProgress(notificationUuid, 50);
  await new Promise((resolve) => setTimeout(resolve, 500));
  setProgress(notificationUuid, 75);
  await new Promise((resolve) => setTimeout(resolve, 500));
  setProgress(notificationUuid, 100);
}

async function downloadReport() {
  currentUuid = showNotification("Downloading report D-23459", {
    type: "progress",
    actionText: "Cancel",
    action: () => {
      dismissNotification(currentUuid);
    },
  });

  try {
    await downloadReportAPI(currentUuid);
    showNotification("Report downloaded", {
      type: "success",
      duration: "medium",
      actionText: "View",
      action: () => {
        console.log("View report clicked!");
      },
      cancelUUID: currentUuid,
    });
  } catch (err) {
    showNotification("Download failed", {
      type: "failure",
      duration: "medium",
      cancelUUID: currentUuid,
    });
  }
}

downloadBtn.addEventListener("_click", downloadReport);
<goa-temp-notification-ctrl></goa-temp-notification-ctrl>

<goa-button version="2" id="download-btn" type="tertiary" leadingicon="download">
  Download report
</goa-button>

Display progress feedback during long-running operations like downloads, showing percentage completion with the ability to cancel and receive success confirmation.

When to use

Use this pattern when:

  • An operation takes more than a few seconds
  • Progress can be measured as a percentage (0-100%)
  • Users need the ability to cancel the operation
  • Success or failure confirmation is needed

Considerations

  • Always provide a cancel option for long operations
  • Show a success notification when complete
  • Use type="progress" for operations with known duration
  • Handle errors gracefully with failure notifications