---
title: Stream hooks
description: Start and cancel typed generic streams without treating them as finite queries.
pagination: false
sidebar:
  icon: radio
---

Use a stream hook to receive a sequence of updates and cancel it when the reader no longer needs them.

> **Before you begin**
>
> Complete [frontend installation and contract pull](/docs/modules/client) before using this guide.
> Keep its selected React/query peers and generated declarations in the frontend package.

## Start a stream

`useStream` exposes `start`, `cancel`, and `reset`. Query-classified streams may opt into `autoStart`;
mutation streams always require an explicit start. Classification controls start/retry policy only.
It does not make a stream a finite cache query.

The low-level client returns a single-consumer `AsyncIterable`. Work starts on the first `next()`, and
breaking iteration cancels upstream work and releases its pinned invocation generation.

## Use the typed hook

```tsx title="src/Report.tsx"
"use client";

/** Consumes the commerce report route as a cancellable typed stream inside the client provider. */
import { useRelkitClient, useStream } from "@relkit/client/react";

/** Starts one report explicitly and displays its validated progress items.
 * @returns Start/cancel controls and visible progress or stream failure. */
export function Report() {
  const { status } = useRelkitClient();
  const report = useStream("GET /reports/:reportId");

  return (
    <section>
      <button
        disabled={status !== "ready"}
        onClick={() => void report.start({ reportId: "daily" })}
      >
        Run report
      </button>
      <button onClick={() => report.cancel()}>Cancel report</button>
      <p role="status">Completed: {report.items.map((item) => item.completed).join(", ")}</p>
      {report.error && <p role="alert">{String(report.error)}</p>}
    </section>
  );
}
```

This selector belongs to the commerce [streaming route](/docs/modules/http/streaming). Its
`streamOf` item schema determines `report.items`, so do not copy a stream selector into an app
that has not declared that route and pulled its contract.

Use [`useAgent`](/docs/modules/ai/streaming#use-from-the-frontend) for durable agent threads,
[`useJobRun`](/docs/modules/jobs/clients#use-from-react) for accepted jobs, and
[`useChannel`](/docs/modules/realtime/react) for shared application updates. Those have separate
identity and recovery contracts. Reference: [`useStream`](/docs/api/client#usestream).

{/* Generated by apps/docs/scripts/generate.ts. Do not edit. */}

**Reference and source**

- [`@relkit/client` API](/docs/api/client)
- [`@relkit/routes` API](/docs/api/routes)

- [`apps/docs/examples/frontend/report.tsx`](https://github.com/rel-kit/relkit/blob/main/apps/docs/examples/frontend/report.tsx)
