> ## Documentation Index
> Fetch the complete documentation index at: https://docs.markpdf.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Streaming and asynchronous jobs

> Upload progress with useConvertFile, streaming with useMarkpdf, and 202 (auto-async).

# Streaming and asynchronous jobs

`@markpdf/react` combines two different things: **upload progress** (inherited from the `useConvertFile` hook, via `XMLHttpRequest`) and **streaming/jobs from API** (inherited from the base client `@markpdf/sdk`, via `useMarkpdf()`).

## Upload progress with `useConvertFile`

```tsx theme={null}
const { convert, status, progress } = useConvertFile();

<input type="file" onChange={(e) => convert(e.target.files![0])} />
{status === "uploading" && <progress value={progress} max={100} />}
```

* `status` goes through `"idle" → "uploading" → "converting" → "success"|"error"`.
* `progress` only makes sense during `"uploading"` (percentage of bytes uploaded). During `"converting"` the server has already received the complete file and is processing it; there is no granular progress from that phase.

This covers `POST /convert` (multipart). It does not apply to `convertFromUrl`, where there are no bytes to upload from the browser.

## Streaming Markdown with `useMarkpdf()`

To consume `/convert/stream` chunk by chunk, use the raw client:

```tsx theme={null}
import { useMarkpdf } from "@markpdf/react";
import { useState } from "react";

function StreamedPreview({ url }: { url: string }) {
  const client = useMarkpdf();
  const [text, setText] = useState("");

  const start = async () => {
    setText("");
    for await (const chunk of client.convertStream({ url }, { slim: true })) {
      setText((prev) => prev + chunk);
    }
  };

  return (
    <>
      <button onClick={start}>Convertir en streaming</button>
      <pre>{text}</pre>
    </>
  );
}
```

## Jobs due to saturation (202)

`useConvertFile().convert()` does not automatically poll queued jobs — if API responds `202`, the hook's `error` will be `MarkpdfError` with the job detail. To handle it, use the raw client with `autoPoll` (the default):

```tsx theme={null}
import { useMarkpdf } from "@markpdf/react";

function ConvertWithAutoPoll({ file }: { file: File }) {
  const client = useMarkpdf();

  const run = async () => {
    // base client's convertFile DOES do auto poll of 202
    const markdown = await client.convertFile(file, { filename: file.name, autoPoll: true });
    console.log(markdown);
  };

  return <button onClick={run}>Convertir</button>;
}
```

<Tip>
  `useConvertFile()` is intended for the common "upload with progress bar" case. If you need explicit handling of `202`/job retries, combine `useMarkpdf()` (base client with `autoPoll`) instead of `useConvertFile()`.
</Tip>

<Warning>
  Results for completed jobs expire in approximately 1 hour. If you save a `jobId` and query `client.getJob(jobId)` later and receive `404`, resend the original conversion.
</Warning>
