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 />
<GoabButton type="tertiary" leadingIcon="download" onClick={downloadReport}>
  Download report
</GoabButton>
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>

<goab-button type="tertiary" leadingIcon="download" (onClick)="downloadReport()">
  Download report
</goab-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
View old example docs