5. Upload and render
<UploadButton> needs endpoint (there is no default path) and takes route, onComplete(results), onError(error), accept, multiple, className, disabled and children (a node or a function of { state, progress, file }). With multiple the files upload one after another and onComplete fires once with every result. The button is unstyled and exposes data-state and aria-busy.
A result is { upload_id, file_slug, status, url, width, height, server_data? }. When processing takes longer than the router's 30-second poll, status is still processing and url is null; the file becomes ready shortly after.
<Img2siteImage host slug chain? kind? alt /> renders an <img> for an unsigned delivery URL; the chain uses the delivery URL grammar. Protected projects need a server-signed URL instead (img2site.signedUrl()).
app/profile/avatar-upload.tsx
// app/profile/avatar-upload.tsx
"use client";
import { useState } from "react";
import { Img2siteImage, UploadButton } from "@img2site/sdk-react";
export function AvatarUpload() {
const [slug, setSlug] = useState<string | null>(null);
return (
<div>
<UploadButton
endpoint="/api/img2site"
route="avatar"
accept="image/*"
onComplete={(results) => setSlug(results[0]?.file_slug ?? null)}
onError={(error) => console.error(error.code, error.message)}
/>
{slug && (
<Img2siteImage
host="my-project.i2scd.net"
slug={slug}
chain="resize/256x256/smart/format/webp"
alt="Your avatar"
width={256}
height={256}
/>
)}
</div>
);
}