# How iloader's Operation Orchestrator Manages Background Tasks: React-Tauri Integration

> Discover how iloader's operation orchestrator manages background tasks using React and Rust with Tauri integration. Learn about its efficient task delegation and UI state synchronization.

- Repository: [Nicholas Sharp/iloader](https://github.com/nab138/iloader)
- Tags: internals
- Published: 2026-09-12

---

**iloader coordinates long-running background tasks through a React-based orchestrator that delegates work to a Rust backend and synchronizes UI state via Tauri event listeners.**

nab138/iloader handles resource-intensive operations like downloading and installing through a lightweight **operation orchestrator** built into its React frontend. This system manages **background tasks** by bridging the JavaScript UI layer with the Rust-powered Tauri backend using an event-driven architecture. The orchestrator tracks progress across discrete steps while keeping the interface responsive through immutable state updates.

## Defining Operation Structures in operations.ts

The orchestrator relies on static operation definitions that declare each task's identity and step sequence. These definitions reside in **[`src/components/operations.ts`](https://github.com/nab138/iloader/blob/main/src/components/operations.ts)**, where each operation object specifies a unique ID, localization keys, and an ordered array of steps.

```typescript
export const installSideStoreOperation: Operation = {
  id: "install_sidestore",
  titleKey: "operations.install_sidestore_title",
  steps: [
    { id: "download", titleKey: "operations.install_sidestore_step_download" },
    { id: "install",  titleKey: "operations.install_sidestore_step_install" },
    { id: "pairing",  titleKey: "operations.install_sidestore_step_pairing" },
  ],
};

```

*(see source)*: `src/components/operations.ts#L39-L58`

This declarative pattern allows the UI to render progress indicators generically while the backend handles step-specific logic.

## Initiating Background Tasks with the startOperation Hook

The core orchestration logic lives in **[`src/App.tsx`](https://github.com/nab138/iloader/blob/main/src/App.tsx)** within the `startOperation` function (lines 84-135). This **React hook** coordinates the lifecycle of background tasks through three distinct phases: state initialization, event subscription, and backend invocation.

```typescript
const startOperation = useCallback(
  async (operation: Operation, params: { [key: string]: any }) => {
    setOperationState({ current: operation, started: [], failed: [], completed: [] });
    const unlistenFn = await listen<OperationUpdate>(
      "operation_" + operation.id,
      (event) => {
        setOperationState((old) => {
          if (!old) return null;
          switch (event.payload.updateType) {
            case "started":
              return { ...old, started: [...old.started, event.payload.stepId] };
            case "finished":
              return { ...old, completed: [...old.completed, event.payload.stepId] };
            case "failed":
              return {
                ...old,
                failed: [...old.failed, { stepId: event.payload.stepId, extraDetails: event.payload.extraDetails }],
              };
          }
          return old;
        });
      },
    );
    try {
      await invoke(operation.id + "_operation", params);   // backend work
      unlistenFn();
    } catch (e) {
      unlistenFn();
      throw e;
    }
  },
  [setOperationState],
);

```

*(see source)*: `src/App.tsx#L84-L135`

### Registering Tauri Event Listeners

When `startOperation` executes, it immediately establishes a **Tauri event listener** using the `listen` function from `@tauri-apps/api/event`. The listener subscribes to a channel named `operation_<operation.id>` (e.g., `operation_install_sidestore`), creating a bidirectional communication pipeline between the Rust backend and React frontend.

The listener remains active until the backend signals completion or an error occurs, at which point `unlistenFn()` cleans up the subscription to prevent memory leaks.

### Invoking Backend Commands

After registering the listener, the orchestrator triggers the actual work by calling `invoke(operation.id + "_operation", params)`. This dispatches a command to the Rust layer in **[`src-tauri/src/main.rs`](https://github.com/nab138/iloader/blob/main/src-tauri/src/main.rs)**, passing parameters like `{ nightly: false, liveContainer: false }` while the UI continues running without blocking.

## Processing Real-Time Progress Updates

As the Rust backend executes **background tasks**, it emits **Tauri events** containing `OperationUpdate` payloads. Each payload includes an `updateType` field with values of `started`, `finished`, or `failed`, plus the corresponding `stepId`.

The `startOperation` listener processes these updates by computing new **immutable state** objects:

- **Started steps**: Appended to the `started` array to activate loading indicators
- **Completed steps**: Added to the `completed` array for success checkmarks  
- **Failed steps**: Pushed to the `failed` array with `extraDetails` for error reporting

This functional state update pattern ensures React efficiently re-renders only the changed step components.

## Visualizing Task Progress in OperationView

The **[`src/components/OperationView.tsx`](https://github.com/nab138/iloader/blob/main/src/components/OperationView.tsx)** component consumes the **OperationState** to render a modal overlay showing real-time progress. It calculates completion status by comparing the lengths of the `started`, `completed`, and `failed` arrays against the total step count.

```typescript
const done =
  (opFailed && operationState.started.length === operationState.completed.length + operationState.failed.length) ||
  operationState.completed.length === operation.steps.length;

```

*(see source)*: `src/components/OperationView.tsx#L24-L33`

The component maps each step to visual states:

- **Waiting**: Gray indicator for steps not yet started
- **Running**: Animated spinner for steps in the `started` array
- **Succeeded**: Green checkmark for steps in the `completed` array
- **Failed**: Red exclamation icon for steps in the `failed` array

```tsx
<div className="operation-step-icon">
  {failed && <FaCircleExclamation className="operation-error" />}
  {!failed && completed && <FaCircleCheck className="operation-check" />}
  {!failed && !completed && started && <div className="loading-icon" />}
  {notStarted && !opFailed && <div className="waiting-icon" />}
</div>

```

*(see source)*: `src/components/OperationView.tsx#L95-L115`

## Example: Triggering the Install Operation

UI components initiate orchestrated tasks by calling `startOperation` with the appropriate operation definition and parameters. For example, installing the stable version of Sidestore invokes:

```tsx
<button
  onClick={() => {
    if (!ensuredLoggedIn() || !ensureSelectedDevice()) return;
    startOperation(installSideStoreOperation, {
      nightly: false,
      liveContainer: false,
    }).catch(e => console.error(e));
  }}
>
  {t("app.sidestore_stable")}
</button>

```

*(see source)*: `src/App.tsx#L10-L13` & `src/App.tsx#L108-L119`

This pattern appears across **[`src/pages/Settings.tsx`](https://github.com/nab138/iloader/blob/main/src/pages/Settings.tsx)** and **[`src/pages/Pairing.tsx`](https://github.com/nab138/iloader/blob/main/src/pages/Pairing.tsx)**, providing consistent task management throughout the application.

## Summary

- **Operation definitions** in [`src/components/operations.ts`](https://github.com/nab138/iloader/blob/main/src/components/operations.ts) declare task structures as static configurations with localization support
- **`startOperation`** in [`src/App.tsx`](https://github.com/nab138/iloader/blob/main/src/App.tsx) initializes **OperationState**, registers **Tauri event listeners**, and invokes Rust commands without blocking the UI
- The orchestrator processes **OperationUpdate** payloads to track step status through immutable state transitions
- **`OperationView`** renders progress visually by comparing state arrays, automatically updating as **background tasks** emit events
- Cleanup functions remove event listeners upon completion or error, preventing memory leaks in long-running sessions

## Frequently Asked Questions

### What triggers a background operation in iloader?

User interactions in pages like **[`src/pages/Settings.tsx`](https://github.com/nab138/iloader/blob/main/src/pages/Settings.tsx)** or **[`src/pages/Pairing.tsx`](https://github.com/nab138/iloader/blob/main/src/pages/Pairing.tsx)** call the `startOperation` function with a specific operation definition (such as `installSideStoreOperation`) and runtime parameters. This initiates the orchestrator's lifecycle: state reset, event listener registration, and backend command invocation.

### How does iloader communicate progress from Rust to React?

The Rust backend emits **Tauri events** on channels named `operation_<id>` (e.g., `operation_install_sidestore`). The React frontend uses Tauri's `listen` function to subscribe to these channels, receiving `OperationUpdate` payloads that contain `updateType` ("started", "finished", or "failed") and the affected `stepId`. These events update **OperationState** through React's `setState` hooks.

### What happens if an operation step fails?

When the backend emits a "failed" update type, the orchestrator appends the step to the `failed` array within **OperationState**, including `extraDetails` for error diagnostics. The `OperationView` component detects this state change and renders error icons while preventing further step progression until the user dismisses the modal or retries the operation.

### Where are operation definitions stored in iloader?

Static operation schemas reside in **[`src/components/operations.ts`](https://github.com/nab138/iloader/blob/main/src/components/operations.ts)**, which exports typed `Operation` objects containing IDs, title localization keys, and step arrays. These definitions serve as blueprints that both the UI and backend reference to maintain consistent task sequencing and messaging across the **operation orchestrator** architecture.