> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://fastpix.com/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://fastpix.com/docs/_mcp/server.

# Set up resumable uploads for Astro

The FastPix [Astro uploader](https://github.com/FastPix/web-uploads-sdk) is an Astro-native component for chunked, resumable video uploads, with drag-and-drop, progress tracking, and pause, resume, and cancel controls. Give `<FastPixUploader />` a signed upload URL, and it uploads the file in resumable chunks and fires a `fastpix-success` event when the upload finishes.

Because it uploads directly to the signed URL from the browser, you do not need to switch Astro to `server` or `hybrid` output or handle `multipart/form-data` on a backend route. It is built on the FastPix resumable web uploads SDK, renders server-side with no visual flicker, and needs no React, Vue, or Svelte, the client runtime is plain custom elements, so it works in any Astro project.

```text
your app ──── upload URL ────▶ <FastPixUploader /> ──── fastpix-success ────▶ your app
```

\


## Install the component

Install the package with your preferred package manager:

```bash
npm install @fastpix/fp-astro-uploader@latest
```

`astro` (v7 or later) is a peer dependency and should already be in your project. No stylesheet import is needed, the styling ships with the component (see [Appearance](#appearance)).

\


## Generate a signed upload URL

The component uploads to a **signed URL**: a pre-authorized, short-lived URL that lets the browser upload directly to FastPix without exposing your API credentials.

To create one, you need a valid **Access Token** and **Secret Key**. See the [Basic Authentication guide](/docs/getting-started/activate-your-account#access-token-permissions) for how to retrieve them. Then call the [Upload media from device](/docs/video-on-demand-api/upload-and-import-videos/direct-upload-video-media) API to get the signed URL.

> **Keep the Direct Upload API call on your server** so your Access Token and Secret Key are never exposed to the browser. The component only needs the signed URL that call returns. See [Upload videos from device](/docs/upload-videos/upload-videos-from-device) for the request shape.

\


## Add the uploader

Import the component in your Astro frontmatter and render it with a signed URL. This is the fastest path, a complete uploader with sensible defaults:

```astro
---
import { FastPixUploader } from "@fastpix/fp-astro-uploader";
---

<FastPixUploader endpoint="https://your-fastpix-upload-url" />
```

That is enough to pick a file and upload it with progress and pause and resume.

\


### Provide the upload URL at upload time

A static string works for the zero-config case above. In practice you create the upload URL once a file is selected. Astro's server-to-client boundary means a function prop cannot cross from frontmatter, so assign it from a client `<script>` through the element's `endpoint` property instead. It receives the selected `File` and returns the URL (it may be async). Here `getSignedUrl` is your own function that returns a FastPix upload URL:

```astro
<FastPixUploader id="up" />

<script>
  import { getUploader } from "@fastpix/fp-astro-uploader/client";
  import { getSignedUrl } from "../lib/get-signed-url";

  const el = await getUploader("#up");
  el.endpoint = getSignedUrl;
</script>
```

\


## Respond to upload events

Listen for `fastpix-*` DOM events to react to the upload lifecycle. All are optional.

```astro
<FastPixUploader id="up" accept="video/*" />

<script>
  import { getUploader } from "@fastpix/fp-astro-uploader/client";
  import { getSignedUrl } from "../lib/get-signed-url";

  const el = await getUploader("#up");
  el.endpoint = getSignedUrl;

  el.addEventListener("fastpix-progress", (e) => console.log("Progress:", e.detail.progress));
  el.addEventListener("fastpix-success", () => console.log("Upload complete"));
  el.addEventListener("fastpix-error", (e) => console.error("Upload error:", e.detail.message));
</script>
```

`fastpix-success` fires when the upload finishes. Anything after that, such as waiting for FastPix to process the media and then playing it, belongs to your application.

| Event                                | `detail`                                    | Fires when                                               |
| ------------------------------------ | ------------------------------------------- | -------------------------------------------------------- |
| `fastpix-file-select`                | `{ file }`                                  | A valid, readable file is picked or dropped.             |
| `fastpix-file-reject`                | `{ file, reason, message }`                 | A file fails `accept`, `maxFileSize`, or cannot be read. |
| `fastpix-upload-start`               | `{ file }`                                  | The upload begins.                                       |
| `fastpix-progress`                   | `{ progress }` (0 to 100)                   | Progress updates.                                        |
| `fastpix-chunk-success`              | `{ chunkNumber, totalChunks?, chunkSize? }` | A chunk finishes.                                        |
| `fastpix-pause`                      | `null`                                      | The upload is paused.                                    |
| `fastpix-resume`                     | `null`                                      | The upload is resumed.                                   |
| `fastpix-abort`                      | `null`                                      | The upload is cancelled.                                 |
| `fastpix-error`                      | `{ message }`                               | The upload fails.                                        |
| `fastpix-success`                    | `null`                                      | The upload completes.                                    |
| `fastpix-state-change`               | `{ state }`                                 | The state changes.                                       |
| `fastpix-offline` / `fastpix-online` | `null`                                      | The browser loses or regains its network connection.     |

\


## Build your own layout

Render the individual components as children to control the layout and styling. Each one binds to the nearest `<FastPixUploader>` automatically, so they work wherever you place them and in any order:

```astro
---
import {
  FastPixUploader,
  FastPixDropZone,
  FastPixStatus,
  FastPixTrack,
  FastPixStartButton,
  FastPixPauseButton,
  FastPixResumeButton,
  FastPixAbortButton,
} from "@fastpix/fp-astro-uploader";
---

<FastPixUploader id="up" autoStart={false}>
  <FastPixDropZone overlay>
    <p>Drag a video here, or click to browse</p>
  </FastPixDropZone>

  <FastPixStatus />
  <FastPixTrack showLabel />

  <FastPixStartButton />
  <FastPixPauseButton />
  <FastPixResumeButton />
  <FastPixAbortButton />
</FastPixUploader>

<script>
  import { getUploader } from "@fastpix/fp-astro-uploader/client";
  import { getSignedUrl } from "../lib/get-signed-url";

  (await getUploader("#up")).endpoint = getSignedUrl;
</script>
```

The child components are `FastPixFilePicker`, `FastPixDropZone`, `FastPixTrack` (the progress bar, `linear` or `radial`), `FastPixStatus` (state text), and the `FastPixStartButton`, `FastPixPauseButton`, `FastPixResumeButton`, and `FastPixAbortButton` controls.

\


## Component props

The `<FastPixUploader>` component accepts the following props:

| Name                | Type                   | Required | Description                                                                                                                       |
| ------------------- | ---------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `endpoint`          | `string`               | Optional | The upload URL. For a URL resolved per file, assign a function to the element's `endpoint` property from a client script instead. |
| `autoStart`         | `boolean`              | Optional | Start uploading as soon as a valid file is available. Default is `true`. Set `false` to require an explicit start.                |
| `accept`            | `string`               | Optional | Allowed file types (for example `"video/*"`, `".mp4"`), enforced for the picker and the drop zone.                                |
| `maxFileSize`       | `number` (in KB)       | Optional | Reject files larger than this before uploading.                                                                                   |
| `chunkSize`         | `number` (in KB)       | Optional | Size of each upload chunk. Minimum 5120 KB (5 MB), maximum 512000 KB (500 MB), in multiples of 256 KB.                            |
| `retryChunkAttempt` | `number`               | Optional | Retry attempts per chunk on failure.                                                                                              |
| `delayRetry`        | `number` (in seconds)  | Optional | Delay between retry attempts.                                                                                                     |
| `disabled`          | `boolean`              | Optional | Disable all interaction. Default is `false`.                                                                                      |
| `size`              | `"sm" \| "md" \| "lg"` | Optional | Overall size of the rendered components. Default is `"md"`.                                                                       |
| `appearance`        | `FastPixAppearance`    | Optional | Appearance values applied as CSS variables (see [Appearance](#appearance)).                                                       |
| `id`                | `string`               | Optional | Element id, required to address the instance from a client `<script>` through `getUploader`.                                      |
| `class`             | `string`               | Optional | Class applied to the root element.                                                                                                |

\


## Control the upload from a script

`getUploader(selector)` resolves the element (awaiting its definition first, so it is safe to call before the page finishes hydrating). The element carries the control methods:

```astro
<script>
  import { getUploader } from "@fastpix/fp-astro-uploader/client";

  const el = await getUploader("#up");
  el.start();
  if (el.getState().status === "error") el.reset();
</script>
```

Available methods: `start()`, `pause()`, `resume()`, `abort()`, `reset()` (clear the file and return to idle), `getState()`, `getFile()`, and `selectFile(file)` (to upload a `File` you obtained from your own picker).

\


## Build a custom UI with /core

To build a completely custom uploader with no provided markup, drive `UploaderController` directly, the same state machine and validation the components use, with no DOM or Astro dependency:

```ts
import { UploaderController } from "@fastpix/fp-astro-uploader/core";

const controller = new UploaderController({
  config: { endpoint: getSignedUrl, autoStart: false },
});
controller.attach(); // starts tracking window online/offline

controller.on("progress", (progress) => console.log(progress));
controller.on("stateChange", (state) => console.log(state));

fileInput.addEventListener("change", (e) => {
  const file = e.target.files?.[0];
  if (file) controller.selectFile(file);
});
startButton.addEventListener("click", () => controller.start());
```

\


## Appearance

There are three ways to style the uploader, and they can be combined:

**1. CSS variables.** Set any `--fastpix-*` variable on the component or globally on `:root`:

```css
fastpix-uploader {
  --fastpix-accent-color: #00d1ff;
  --fastpix-radius: 12px;
  --fastpix-surface: #111;
}
```

**2. The `appearance` prop.** The same values as a typed object, when you would rather not write CSS:

```astro
<FastPixUploader
  endpoint="https://your-fastpix-upload-url"
  appearance={{ accentColor: "#00d1ff", radius: "12px" }}
/>
```

**3. `class` and `style`.** Every component accepts these for full control.

The root also carries the current state as a `data-fastpix-state` attribute (`idle`, `ready`, `resolving`, `uploading`, `paused`, `error`, `success`), so you can style any phase in plain CSS. The `size` prop (`"sm" | "md" | "lg"`) scales padding, spacing, text, and controls together.

\


## Accessibility

* Status text is announced to assistive technology (`role="status"`, `aria-live="polite"`), so screen-reader users hear state and progress changes.
* All controls, including the drop zone, are real buttons: keyboard focusable, activatable with Enter or Space, and shown with a visible focus ring.
* The `disabled` state is reflected for both pointer and assistive interaction.

\


## File access on mobile

Sometimes a browser hands your app a `File` it cannot actually read. This happens most on Android with `accept="video/*"`, which opens the Photos or Gallery app and may return a sandboxed file. The component guards against this: it checks that the bytes are readable before accepting a file, and rejects an unreadable one through `fastpix-file-reject` with `reason: "unreadable"` and a message telling the user to pick the video from their device's file manager instead.

> **Note:** To reduce how often this happens, broaden `accept` (for example `"video/*,audio/*"`) or omit it, which makes Android open the system file manager rather than the media picker. The readability check is always on as a safety net.

\


#### Frequently asked questions

A resumable upload is a way to transfer a file that lets you pause, or recover from a network drop or interruption, without starting over from the beginning. The file is sent in sequential chunks, and the upload continues from the last confirmed chunk instead of restarting. It is the same pattern used by standards like the open-source tus protocol. The Astro uploader does this for you and exposes pause, resume, and progress.

A signed upload URL is a short-lived, pre-authorized URL that lets the browser upload directly to FastPix without carrying your Access Token or Secret Key. You create it on your server with the Direct Upload API and pass only the URL to the component, so your credentials never reach the browser.

No. The client runtime is plain custom elements, so it works in any Astro project regardless of which UI framework you use elsewhere, or none at all.

Yes. Compose the individual child components (drop zone, progress track, status, and control buttons) for a custom layout, or drive `UploaderController` from `/core` to build a fully custom UI with no provided markup.

\


## What's next

* [Set up resumable uploads for Web](/docs/upload-videos/set-up-resumable-uploads-for-web) for the underlying JavaScript SDK.
* [Upload videos from device](/docs/upload-videos/upload-videos-from-device) for the Direct Upload API request shape.
* [Play your videos](/docs/video-on-demand/play-your-videos) once FastPix has processed the upload.