Skip to navigation

Set up resumable uploads for Astro

Add a drop-in resumable video upload component to an Astro site with the FastPix Astro uploader, with drag-and-drop, progress, and pause and resume.

The FastPix Astro uploader 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.

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

Install the component

Install the package with your preferred package manager:

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).


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 for how to retrieve them. Then call the Upload media from device 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 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:

---
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:

<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.

<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.

EventdetailFires 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-pausenullThe upload is paused.
fastpix-resumenullThe upload is resumed.
fastpix-abortnullThe upload is cancelled.
fastpix-error{ message }The upload fails.
fastpix-successnullThe upload completes.
fastpix-state-change{ state }The state changes.
fastpix-offline / fastpix-onlinenullThe 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:

---
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:

NameTypeRequiredDescription
endpointstringOptionalThe upload URL. For a URL resolved per file, assign a function to the element’s endpoint property from a client script instead.
autoStartbooleanOptionalStart uploading as soon as a valid file is available. Default is true. Set false to require an explicit start.
acceptstringOptionalAllowed file types (for example "video/*", ".mp4"), enforced for the picker and the drop zone.
maxFileSizenumber (in KB)OptionalReject files larger than this before uploading.
chunkSizenumber (in KB)OptionalSize of each upload chunk. Minimum 5120 KB (5 MB), maximum 512000 KB (500 MB), in multiples of 256 KB.
retryChunkAttemptnumberOptionalRetry attempts per chunk on failure.
delayRetrynumber (in seconds)OptionalDelay between retry attempts.
disabledbooleanOptionalDisable all interaction. Default is false.
size"sm" | "md" | "lg"OptionalOverall size of the rendered components. Default is "md".
appearanceFastPixAppearanceOptionalAppearance values applied as CSS variables (see Appearance).
idstringOptionalElement id, required to address the instance from a client <script> through getUploader.
classstringOptionalClass 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:

<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:

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:

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:

<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

What is a resumable upload?

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.

What is a signed upload URL?

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.

Does the uploader need React, Vue, or Svelte?

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.

Can I build my own UI instead of the default component?

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