# Tracking Model Loading Progress with the `progress_callback` Option in Transformers.js

> Track model loading progress in Transformers.js with progress_callback. Receive granular progress events for real-time download tracking and monitor overall completion percentage easily.

- Repository: [Hugging Face/transformers.js](https://github.com/huggingface/transformers.js)
- Tags: how-to-guide
- Published: 2026-03-03

---

**The `progress_callback` option in Transformers.js enables real-time tracking of model file downloads through a type-safe callback that receives granular `ProgressInfo` events including per-file bytes loaded, total size, and overall completion percentage.**

When loading ONNX models from the Hugging Face Hub using the [`huggingface/transformers.js`](https://github.com/huggingface/transformers.js/blob/main/huggingface/transformers.js) library, developers can monitor download status in real-time by supplying a `progress_callback` function. This mechanism provides fine-grained visibility into the loading pipeline, reporting progress for individual files like `model.onnx` and [`tokenizer.json`](https://github.com/huggingface/transformers.js/blob/main/tokenizer.json) as well as aggregate completion percentages. The implementation leverages a discriminated union type system defined in the core utilities to ensure type safety across both browser and Node.js environments.

## How the Progress Callback Architecture Works

### Source Files and Type Definitions

The callback system originates in [`src/utils/core.js`](https://github.com/huggingface/transformers.js/blob/main/src/utils/core.js), where the `ProgressCallback` type is defined as a function receiving a `ProgressInfo` discriminated union (lines 70-78). This union includes status variants: `initiate`, `download`, `progress`, `done`, `ready`, and `progress_total`. The `dispatchCallback` helper (lines 89-91) serves as the central dispatcher that invokes the callback when present.

In [`src/utils/hub.js`](https://github.com/huggingface/transformers.js/blob/main/src/utils/hub.js), the `PretrainedOptions` interface includes the optional `progress_callback` property (lines 25-28). The high-level entry point `getModelFile` (lines 64-71) forwards this callback to lower-level loaders, while `loadResourceFile` orchestrates the actual fetching logic and emits per-file progress events.

### The Event Flow from HTTP to Callback

When `pipeline()` initiates model loading, the flow proceeds through several stages. First, `loadResourceFile` checks the cache; on a miss, it fetches the remote URL. The `readResponse` function in [`src/utils/hub/utils.js`](https://github.com/huggingface/transformers.js/blob/main/src/utils/hub/utils.js) (lines 84-106) streams the response body, calculating loaded bytes and percentage completion. Each chunk triggers a `{status: 'progress', loaded, total, progress}` event through `dispatchCallback`. Upon completion, a `{status: 'done'}` event fires.

For multi-file models, [`src/pipelines.js`](https://github.com/huggingface/transformers.js/blob/main/src/pipelines.js) (lines 124-166) aggregates individual file progress into `progress_total` events, providing a unified percentage across all required assets.

## Implementing `progress_callback` in Practice

### Console Logging for Debugging

The simplest implementation logs per-file progress to the console. This approach captures both individual file downloads and the aggregate `progress_total` event.

```javascript
import { pipeline } from '@huggingface/transformers';

function consoleProgress(info) {
  if (info.status === 'progress') {
    console.log(
      `${info.name} – ${info.file}: ${info.progress.toFixed(1)}% (${info.loaded}/${info.total} bytes)`
    );
  } else if (info.status === 'progress_total') {
    console.log(`Overall: ${info.progress.toFixed(1)}%`);
  } else if (info.status === 'done') {
    console.log(`${info.name} – ${info.file} loaded`);
  }
}

const pipe = await pipeline(
  'text-classification', 
  'Xenova/distilbert-base-uncased-finetuned-sst-2-english', 
  { progress_callback: consoleProgress }
);

```

This callback receives events from `loadResourceFile` in [`hub.js`](https://github.com/huggingface/transformers.js/blob/main/hub.js) (lines 84-90) and the aggregation logic in [`pipelines.js`](https://github.com/huggingface/transformers.js/blob/main/pipelines.js).

### React Progress Bar Integration

For UI applications, the callback can drive progress indicators. The following React component maintains state for each file's percentage:

```tsx
import React, { useState, useEffect } from 'react';
import { pipeline } from '@huggingface/transformers';

export default function ModelLoader() {
  const [progress, setProgress] = useState({});

  const onProgress = (info) => {
    if (info.status === 'progress') {
      setProgress((p) => ({
        ...p,
        [info.file]: info.progress,
      }));
    }
  };

  useEffect(() => {
    (async () => {
      await pipeline('sentiment-analysis', 'Xenova/bert-base-uncased', {
        progress_callback: onProgress,
      });
    })();
  }, []);

  return (
    <div>
      {Object.entries(progress).map(([file, pct]) => (
        <div key={file}>
          <p>{file}</p>
          <div style={{ background: '#eee', width: '100%', height: '8px' }}>
            <div
              style={{
                background: '#3b82f6',
                width: `${pct}%`,
                height: '100%',
              }}
            />
          </div>
        </div>
      ))}
    </div>
  );
}

```

The per-file `progress` values originate from the `readResponse` streaming logic in [`hub/utils.js`](https://github.com/huggingface/transformers.js/blob/main/hub/utils.js) (lines 84-106), while the React state updates mirror patterns found in the official React tutorial documentation.

### Web Worker Message Passing

To avoid blocking the main thread during large model downloads, forward progress events through a Web Worker:

```javascript
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ type: 'load', model: 'Xenova/gpt2' });

worker.onmessage = (e) => {
  if (e.data.status === 'progress_total') {
    console.log(`Overall ${e.data.progress.toFixed(1)}%`);
  }
};

```

```javascript
// worker.js
import { pipeline } from '@huggingface/transformers';

self.onmessage = async (e) => {
  if (e.data.type === 'load') {
    await pipeline('text-generation', e.data.model, {
      progress_callback: (info) => self.postMessage(info),
    });
    self.postMessage({ status: 'ready' });
  }
};

```

This pattern utilizes the `dispatchCallback` helper in [`core.js`](https://github.com/huggingface/transformers.js/blob/main/core.js) (lines 89-91) to serialize `ProgressInfo` objects across thread boundaries.

## Understanding Progress Event Types

### Per-File vs. Aggregate Progress

The callback receives two distinct progress granularities. **Per-file events** (`status: 'progress'`) originate from `loadResourceFile` and `readResponse`, reporting individual asset downloads with properties like `name`, `file`, `loaded`, and `total`. These events enable detailed per-file progress bars as shown in the React example.

**Aggregate events** (`status: 'progress_total'`) are computed in [`src/pipelines.js`](https://github.com/huggingface/transformers.js/blob/main/src/pipelines.js) (lines 124-166), combining multiple file downloads into a single percentage. This event includes a `files` map tracking each asset's byte count, providing a holistic view of model readiness.

## Summary

- The `progress_callback` option accepts a function conforming to the `ProgressCallback` type defined in [`src/utils/core.js`](https://github.com/huggingface/transformers.js/blob/main/src/utils/core.js) (lines 70-78).
- **Per-file progress** events stream from `readResponse` in [`src/utils/hub/utils.js`](https://github.com/huggingface/transformers.js/blob/main/src/utils/hub/utils.js) (lines 84-106), reporting byte-level download status.
- **Aggregate progress** events (`progress_total`) are generated in [`src/pipelines.js`](https://github.com/huggingface/transformers.js/blob/main/src/pipelines.js) (lines 124-166) to indicate overall loading completion.
- The callback flows through `PretrainedOptions` in [`src/utils/hub.js`](https://github.com/huggingface/transformers.js/blob/main/src/utils/hub.js) (lines 25-28) and is dispatched via `dispatchCallback` in [`src/utils/core.js`](https://github.com/huggingface/transformers.js/blob/main/src/utils/core.js) (lines 89-91).
- Implementation patterns range from simple console logging to complex UI bindings in React or Web Worker architectures.

## Frequently Asked Questions

### What parameters does the progress callback receive?

The callback receives a `ProgressInfo` object, a discriminated union with a `status` property indicating the event type. For `progress` events, it includes `name` (model ID), `file` (filename), `loaded` (bytes received), `total` (total bytes), and `progress` (percentage). For `progress_total` events, it provides aggregate `loaded`, `total`, and a `files` map containing per-file byte counts, as implemented in [`src/pipelines.js`](https://github.com/huggingface/transformers.js/blob/main/src/pipelines.js) (lines 124-166).

### Does the progress callback work with cached models?

Yes. When a model file is served from the cache, `storeCachedResource` in [`src/utils/hub.js`](https://github.com/huggingface/transformers.js/blob/main/src/utils/hub.js) (lines 96-108) re-wraps the callback to emit a final `progress` event showing 100% completion. This ensures consistent UI updates regardless of whether the data originates from the network or local storage.

### Can I use progress_callback in Node.js environments?

Absolutely. The progress system relies on standard web APIs (`fetch` and `Response.body` streaming) that Transformers.js polyfills for Node.js. The `readResponse` implementation in [`src/utils/hub/utils.js`](https://github.com/huggingface/transformers.js/blob/main/src/utils/hub/utils.js) handles both browser and Node.js streaming contexts, ensuring `progress` events fire correctly in either environment.

### How do I track progress for specific files versus total loading?

Inspect the `status` property of the `ProgressInfo` object. When `status` equals `'progress'`, the event represents a single file download (e.g., `model.onnx`). When `status` equals `'progress_total'`, the event represents the aggregate progress across all model files, computed by the pipeline's aggregation logic in [`src/pipelines.js`](https://github.com/huggingface/transformers.js/blob/main/src/pipelines.js).