Headless / Files & documents
Upload files
Compose file upload with storage permissions, loading and failure states.
50 public parts · 3examples
@slices.sh/file-storage/headlessView as Markdown ↗Your design. Our behavior.
Explore the tree to see what each part controls. Gates reveal the relevant state; roots outline their scope.
Upload.Root · Scope
Installation
Choose what your component needs. Run these commands in an initialized Next.js app.
Before you start
The steps below run in a Next.js app with shadcn set up (it has a components.json). In an existing Next.js app, such as one from create-next-app, set shadcn up first:
npx shadcn@latest init -t next -b base -p novaOr start fresh: the slices CLI creates the app and sets it up.
npx slices.sh installInstall the service registry
Adds services/index.ts, which import "@/services" loads. A provider setup writes the same file, so skip this if your app has one.
npx add servicesInstall the headless library
npmAdd the React parts to your app, then compose them with your own elements.
npm install @slices.sh/file-storage@^0.2.7Import from @slices.sh/file-storage/headless.
Install the service
Connect the parts to a provider. Skip this step if this service is already configured in your app.
npx add file-storage.localAdds service configuration and API routes to your app, with the provider dependencies. For a headless composition, add import "@/services" after "use client".
The slices CLI installs the service and provisions its required resources in one command.
Provider setup notes
- Add .data/ to .gitignore: the bytes and the index live there.
- This store lives on one disk. A serverless deployment loses the files between requests, so deploy with @slices.sh/file-storage.s3.
- A screen works in a bucket, a private corner of the config ("files" by default). The config lists the ones its screens work in (`buckets`, which installs grow); list more, or a prefix such as "playground/", in `buckets` in services/file-storage/rules.ts. Any other is not-found.
- Set maxUploadBytes and accept in services/file-storage/default.config.ts: nothing is capped until you do.
Start small
Parts bring the behavior. Use their default elements, or supply your own with render.
"use client"// services/index.ts comes from https://slices.sh/r/services.json (or any provider setup)import "@/services"import { Upload } from "@slices.sh/file-storage/headless"export function Example() { return ( <Upload.Root> <Upload.DropZone> <Upload.Picker aria-label="Choose files" /> Drop files here </Upload.DropZone> <Upload.Uploads> <Upload.Row> <Upload.Name /> <Upload.Progress /> <Upload.Cancel>Cancel</Upload.Cancel> </Upload.Row> </Upload.Uploads> </Upload.Root> )}The provider controls storage. Local storage works without a cloud account.
API reference
Every public part used by this experience is listed below. Gates render children without adding an element. Element parts accept native element props and render; function children receive the state declared in their props. Scopes provide context rather than a visual element.
Upload.Rootscope
The queue, its limits, and its field registration when it has a name inside a Composer.
Props
12 documented props
| Prop | Type | Default |
|---|---|---|
nameThe key of this value in a Composer's message. With a name, inside a Composer, the uploader is a field: its value is the finished files, and it holds the submit while one uploads. Without one, or outside a Composer, it is a plain uploader. | string | — |
enabledLeft out or | Enabled | — |
configThe file-storage config, | string | — |
bucket | string | — |
pathThe directory files land in. | string | "/" |
maxSizeBytes. A larger file is turned away before anything is sent. The server's own limit, in
| number | — |
maxFiles | number | — |
acceptAn | string | — |
multipartFiles from 5 MiB go up in resumable parts wherever the backend has them, so a large file survives a dropped connection and can be paused. On unless turned off: a screen that had to opt in sent a 2 GB file as one request. A backend without parts is sent whole. | boolean | true |
includeHiddenHidden files inside a folder ( | boolean | false |
ifExistsWhat a name that is taken does: replace, keep both, refuse, or | IfExists | "ask" | — |
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-root.tsx
Upload.Availablegate
Draws its children only where `Upload.Picker` would draw an input: a file service is there and the root is on. It exists so what surrounds the paperclip goes with it: a label left behind an absent picker is a word with nothing to open. → docs/rules/capability-honesty.md
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/available.tsx
Upload.DropZonecontent · <div>
The surface files are dropped on. `data-active` while files are over it, and `data-invalid` while none of what is over it is something it takes. Any element can be the surface, a part included: `render={<Upload.Composer />}` makes the whole message box the target, so a file let go over the words, the attachments or the buttons is attached — rather than only over a strip inside it that nobody can see. No `role` and no `tabIndex` of its own, on purpose. The zone is the biggest thing on the screen and it does have to be reachable, and the way to do that is to make it the `<label>` of the picker inside it — `render={<label />}` — so the whole area opens the dialog, the input is what takes focus, and the label's own text is its accessible name. A `<div>` given `role="button"` and a keydown handler would announce a button that is not one and, with an input inside it, one that may not hold anything interactive either. What a person using a screen reader is told about the drag itself is `Upload.Announcer`: `data-active` is a colour, and a colour is not an announcement.
Required scope
Upload.DropZone must be inside <Upload.Root>.
Props
4 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
pasteFiles pasted anywhere inside are attached too: the way people attach a screenshot. | boolean | false |
longPasteWith | number | false | 2000 |
childrenContent rendered inside this part. | ReactNode | ((state: { over: boolean; refusing: boolean }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/drop-zone.tsx
Upload.Pickerinput · <input>
The file input itself. It stays in the DOM rather than being created on click, because a label pointing at it is the one way to open a file dialog that works with a keyboard and says what it opens. Hiding it is the skin's business; an input with `display: none` is still focusable by its label. Its `accept` is the root's, so the dialog offers what the queue will take — and on a phone `"image/*"` is what puts the camera in it. What is chosen anyway still goes past the guard: every dialog has a way to show all files. Given `render={<Button>Attach</Button>}` it is that button instead, which opens the dialog of a hidden input it keeps beside it. The button gets `className`, `ref` and the rest; the input gets `multiple`, `accept`, `capture`, `name` and `disabled`. An element with nothing inside, like `render={<Input />}` or a skin's input, is still the input: a button has a label.
Required scope
Upload.Picker must be inside <Upload.Root>.
Props
3 documented props · Native <input> attributes supported
| Prop | Type | Default |
|---|---|---|
directory | boolean | false |
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/picker.tsx
Upload.Limitscontent · <p>
What this screen takes, said before anybody tries: how big, and what kinds. The numbers are the root's, which are the lower of the screen's and the server's, so what is said here is what is enforced — never a courtesy the server then contradicts. Nothing at all where there is no limit of either kind: "Any file" is a sentence about nothing. → docs/rules/capability-honesty.md `types` are the `accept` entries as they were written, `image/*` and `.pdf`; turning them into "images or PDFs" is the pattern's, because it is words. → docs/rules/words-in-copy-looks-in-skin.md
Required scope
Upload.Limits must be inside <Upload.Root>.
Props
2 documented props · Native <p> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: LimitsSaid) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/limits.tsx
Upload.CountLimitcontent · <p>
The drop surface with data-active and data-invalid, the gate while files are over it, the file input (a folder picker with directory, or a button of your own: render={<Button>Attach</Button>} opens a hidden input), the limits in words (nothing where there is none), and the count limit when maxFiles is set.
Required scope
Upload.CountLimit requires Upload.Root
Props
2 documented props · Native <p> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { max: number }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-count-limit.tsx
Upload.Uploadsscope
Every upload in the queue, each in its own scope.
Required scope
File.Uploads must be inside <File.Upload>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/uploads.tsx
Upload.Rowcontent · <li>
One upload's row. `data-state` is where it is, which is what a skin colours by, and `data-kind` what it is, which is what a skin draws an icon for.
Required scope
File.UploadRow must be inside <File.Uploads>.
Props
2 documented props · Native <li> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { state: UploadState }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-row.tsx
Upload.Thumbnailcontent · <img>
The picture an upload is of, drawn from the file on this device. There from the moment the file is chosen, rather than once the store has it back, and for as long as the upload is in the queue. Nothing at all for anything that is not an image a browser can draw: the icon beside it is the skin's, drawn from the row's `data-kind`, and this part's answer to "no picture" is to render none. → docs/rules/capability-honesty.md
Required scope
Upload.Thumbnail must be inside <Upload.Uploads>.
Props
2 documented props · Native <img> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-thumbnail.tsx
Upload.Namecontent · <span>
The tray that is present while the queue holds anything, one scope per upload, the row with data-state and data-kind, the picture read from the device, name, size, the folders a file came from, the totals, and the live announcement.
Required scope
File.UploadName must be inside <File.Uploads>.
Props
2 documented props · Native <span> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-name.tsx
Upload.Sizecontent · <span>
How big the file is. Known before a byte is sent, which is why it is worth showing.
Required scope
Upload.Size must be inside <Upload.Uploads>.
Props
2 documented props · Native <span> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-size.tsx
Upload.Locationcontent · <span>
The folders a file came from, `holiday/friday`, so a queue of a dropped folder's files is not forty names that could each be anywhere. The whole destination is its `title`. Nothing for a file that was chosen on its own, because a line that said "/" under every row would say nothing. → docs/rules/capability-honesty.md
Required scope
Upload.Location must be inside <Upload.Uploads>.
Props
2 documented props · Native <span> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-location.tsx
Upload.Sendinggate
This one is still on its way. What a progress bar is for. Once a file has landed the bar is full and stays full, which reads as still working — so the row shows its size instead, and the bar is inside this.
Required scope
Upload.Sending must be inside <Upload.Uploads>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/sending.tsx
Upload.Progresscontent · <progress>
How far one upload has got, as a real `<progress>`. `value` is left off while nothing has been reported, which is what makes a progress bar indeterminate — a transport that cannot count bytes then says so, instead of sitting at zero as if it were stuck. The file's name is its accessible name, unless the pattern gives it a better one. A screen with five bars on it reads as "40 percent, 12 percent, 90 percent" otherwise, and which file each belongs to is the one thing a person cannot see.
Required scope
File.Progress must be inside <File.Uploads>.
Props
2 documented props · Native <progress> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { sent: number; total: number }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/progress.tsx
Upload.Ringcontent · <div>
Gates for still on its way, not yet stored and arrived; the bar and the ring (indeterminate until bytes are reported); speed and time left; retrying after a dropped connection; finalizing after the last byte; and the refusal with its error slug.
Required scope
Upload.Ring requires Upload.Uploads.
Props
2 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-ring.tsx
Upload.Cancelaction · <button>
Stops one upload, and is not rendered once it has stopped on its own. The file's name is in `state`: a queue of twenty files is twenty buttons called "Stop this upload" otherwise, with nothing saying which one stops which.
Required scope
File.CancelUpload must be inside <File.Upload>.
File.CancelUpload must be inside <File.Uploads>.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { name: string }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/cancel-upload.tsx
Upload.Pauseaction · <button>
Pause and resume one upload or all of them, keeping confirmed parts, and the uploads a reload interrupted, to resume by choosing the same files again. All render nothing for ordinary uploads or a provider without multipart.
Capability: resumable
Required scope
Upload.Pause requires Upload.Root
Upload.Pause requires Upload.Uploads
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/pause-upload.tsx
Upload.Uploadinggate
Something is going up. `count` is how many, for a line that says so.
Required scope
File.Uploading must be inside <File.Upload>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { count: number }) => ReactNode) | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/uploading.tsx
Upload.Summarycontent · <p>
The tray that is present while the queue holds anything, one scope per upload, the row with data-state and data-kind, the picture read from the device, name, size, the folders a file came from, the totals, and the live announcement.
Required scope
Upload.Summary requires Upload.Root.
Props
2 documented props · Native <p> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { count: number; sent: string; total: string; pending: number; failed: number; percent: number }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-summary.tsx
Upload.Speedcontent · <span>
Gates for still on its way, not yet stored and arrived; the bar and the ring (indeterminate until bytes are reported); speed and time left; retrying after a dropped connection; finalizing after the last byte; and the refusal with its error slug.
Required scope
Upload.Speed requires Upload.Uploads.
Props
2 documented props · Native <span> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { rate: string; bytesPerSecond: number }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-speed.tsx
Upload.Etacontent · <span>
Gates for still on its way, not yet stored and arrived; the bar and the ring (indeterminate until bytes are reported); speed and time left; retrying after a dropped connection; finalizing after the last byte; and the refusal with its error slug.
Required scope
Upload.Eta requires Upload.Uploads.
Props
2 documented props · Native <span> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { seconds: number }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-eta.tsx
Upload.PauseAllaction · <button>
Pause and resume one upload or all of them, keeping confirmed parts, and the uploads a reload interrupted, to resume by choosing the same files again. All render nothing for ordinary uploads or a provider without multipart.
Capability: resumable
Required scope
Upload.PauseAll requires Upload.Root
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/pause-all-uploads.tsx
Upload.QuotaMetercontent · <meter>
The bucket's use as words and as a meter, and the gate when it is full. All render nothing where the provider does not report capacity.
Capability: quota
Required scope
Upload.QuotaMeter requires Upload.Root.
Props
2 documented props · Native <meter> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/quota-meter.tsx
Upload.Quotacontent · <p>
The bucket's use as words and as a meter, and the gate when it is full. All render nothing where the provider does not report capacity.
Capability: quota
Required scope
Upload.Quota requires Upload.Root.
Props
2 documented props · Native <p> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { used: string; limit: string; percent: number }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/quota.tsx
Upload.Overgate
Files are being carried over the drop zone: where "Drop to attach" goes. A gate rather than a colour, so what is said while a file is in the air can be a sentence — and a different one when nothing being carried is something this zone takes. Whatever it draws over the zone should let the pointer through, or the zone loses the drag to it.
Required scope
Upload.Over must be inside <Upload.Root>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { refusing: boolean }) => ReactNode) | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/over.tsx
Upload.Emptygate
Nothing has been dropped yet: where the invitation to drop something goes.
Required scope
File.QueueEmpty must be inside <File.Upload>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/queue-empty.tsx
Upload.Completedgate
Gates for still on its way, not yet stored and arrived; the bar and the ring (indeterminate until bytes are reported); speed and time left; retrying after a dropped connection; finalizing after the last byte; and the refusal with its error slug.
Required scope
Upload.Completed requires Upload.Uploads.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/completed.tsx
Upload.Removeaction · <button>
Takes one file off the list, whatever state it is in. Not `Upload.Cancel`, which stops a file on its way and leaves the row there to be tried again. This is the one a person reaches for when the file is already up and they have changed their mind about attaching it — so it renders for every state, including the ones where there is nothing left to stop. The file's name is in `state`, because five of these called "Remove this attachment" are five buttons nobody listening can tell apart. → docs/rules/words-in-copy-looks-in-skin.md
Required scope
File.RemoveUpload must be inside <File.Upload>.
File.RemoveUpload must be inside <File.Uploads>.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { name: string }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/remove-upload.tsx
Upload.Donegate
Everything the queue set out to send has arrived. The mirror of `Upload.Uploading`, and not the same as "nothing is going up": a queue nobody has put anything in has finished nothing, so this stays shut until at least one upload has been through it. That is what lets a screen say "three files added" without saying it to somebody who has added none. Cancelled and failed uploads are ended but they are not done, so neither counts here — the queue is finished with them, and the person is not. → docs/rules/capability-honesty.md
Required scope
File.Done must be inside <File.Upload>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { count: number }) => ReactNode) | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/done.tsx
Upload.ClearFinishedaction · <button>
Forgets everything that ended, one way or another — the files that were turned away at the door included, which ended before they began. Nothing ended, nothing rendered.
Required scope
Upload.ClearFinished must be inside <Upload.Root>.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/clear-finished.tsx
Upload.Failedgate
This upload was refused, with the reason. A file that is too large and a bucket that is not yours want different sentences: one is about the file, the other about the person. → docs/rules/capability-honesty.md The name is in the state too, for a sentence said away from the row it belongs to — under a tray of chips, where a chip has no room for one.
Required scope
Upload.Failed must be inside <Upload.Uploads>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { error: FileErrorSlug; name: string }) => ReactNode) | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-failed.tsx
Upload.Retryaction · <button>
Sends one again. Rendered for a failure and for a cancel, because both are a file the person still has and may have changed their mind about.
Required scope
File.RetryUpload must be inside <File.Upload>.
File.RetryUpload must be inside <File.Uploads>.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/retry-upload.tsx
Upload.Incompletegate
This upload is not a stored file yet. Its job is to keep one file from being on screen twice. A list that shows what is in the bucket *and* what is going up has, for a moment after the last byte lands, both — the row the queue is still holding and the record the reload just brought back. Wrapping the queue row in this hands each file over to the other list exactly once. Cancelled and failed uploads stay inside it: neither is a stored file, and both still have something to offer — a reason, and another go.
Required scope
File.Incomplete must be inside <File.Uploads>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/incomplete.tsx
Upload.RetryAllaction · <button>
Stop one on its way, send a failed one again, take one off the list, retry every failure, and forget what ended.
Required scope
Upload.RetryAll requires Upload.Root.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/retry-all.tsx
Upload.Retryinggate
Gates for still on its way, not yet stored and arrived; the bar and the ring (indeterminate until bytes are reported); speed and time left; retrying after a dropped connection; finalizing after the last byte; and the refusal with its error slug.
Required scope
Upload.Retrying requires Upload.Uploads.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-retrying.tsx
Upload.Processinggate
Gates for still on its way, not yet stored and arrived; the bar and the ring (indeterminate until bytes are reported); speed and time left; retrying after a dropped connection; finalizing after the last byte; and the refusal with its error slug.
Required scope
Upload.Processing requires Upload.Uploads.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-processing.tsx
Upload.Resumeaction · <button>
Pause and resume one upload or all of them, keeping confirmed parts, and the uploads a reload interrupted, to resume by choosing the same files again. All render nothing for ordinary uploads or a provider without multipart.
Capability: resumable
Required scope
Upload.Resume requires Upload.Root
Upload.Resume requires Upload.Uploads
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/resume-upload.tsx
Upload.ResumeAllaction · <button>
Pause and resume one upload or all of them, keeping confirmed parts, and the uploads a reload interrupted, to resume by choosing the same files again. All render nothing for ordinary uploads or a provider without multipart.
Capability: resumable
Required scope
Upload.ResumeAll requires Upload.Root
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/resume-all-uploads.tsx
Upload.Recoverycontent · <p>
Pause and resume one upload or all of them, keeping confirmed parts, and the uploads a reload interrupted, to resume by choosing the same files again. All render nothing for ordinary uploads or a provider without multipart.
Capability: resumable
Required scope
Upload.Recovery requires Upload.Root
Props
2 documented props · Native <p> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { count: number; names: string }) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-recovery.tsx
Upload.Refusedscope
Every file the last drop, pick or paste turned away, each in its own scope. Apart from the queue on purpose: a file refused for its size never went anywhere, and a row for it among the uploads would offer "Try again" on something no try could change.
Required scope
Upload.Refused must be inside <Upload.Root>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/refused.tsx
Upload.Refusalcontent · <li>
Why one file was turned away, with everything a sentence about it needs. The file's size beside the limit, and the kinds that are taken, because "that file is over the limit" leaves a person guessing what the limit was. `data-state` is the reason, for a skin that marks the two differently. The sentence is the pattern's.
Required scope
Upload.Refusal must be inside <Upload.Refused>.
Upload.Refusal must be inside <Upload.Root>.
Props
2 documented props · Native <li> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: RefusalSaid) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/refusal.tsx
Upload.QuotaExceededgate
The bucket's use as words and as a meter, and the gate when it is full. All render nothing where the provider does not report capacity.
Capability: quota
Required scope
Upload.QuotaExceeded requires Upload.Root.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/quota-exceeded.tsx
Upload.Offlinegate
The browser offline (queued files wait), the service missing under enabled="optional", and a production server that answers config-missing.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/offline.tsx
Upload.MissingConfigurationgate
The server this screen keeps its files on answered that it lacks configuration. The family's own name for the shared gate, with the service already chosen: a screen that draws files should not have to know which string the file routes answer with, and another service's trouble (a transcription key) is not its notice. Not `Unavailable`, which is no backend installed at all: that has a command to run, this has a log to read. → docs/rules/missing-configuration-is-said.md
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { scopes: readonly string[] }) => ReactNode) | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/missing-configuration.tsx
Upload.Traycontent · <div>
The tray that is present while the queue holds anything, one scope per upload, the row with data-state and data-kind, the picture read from the device, name, size, the folders a file came from, the totals, and the live announcement.
Required scope
Upload.Tray requires Upload.Root.
Props
2 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/upload-tray.tsx
Upload.Announcercontent · <div>
What the queue is doing, for somebody who cannot see it. Everything else about an upload is on screen and nowhere else: a bar fills, a row turns red, a footer counts. A screen reader announces none of that, so a person using one drops a file and hears silence — and hears the same silence when it is refused. `role="status"` with `aria-live="polite"` waits for a gap rather than interrupting, and the text is counts rather than events so that twenty files landing is one sentence and not twenty. A file turned away before it was sent counts as refused along with the ones the server refused: to the person, both are a file that did not go. The words are the pattern's: what to say about three files and one refusal is copy. → docs/rules/words-in-copy-looks-in-skin.md
Required scope
Upload.Announcer must be inside <Upload.Root>.
Props
2 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: QueueSaid) => ReactNode) | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/announcer.tsx
Upload.Composeraction · <form>
The form a message and its attachments are sent from. Paste is handled here rather than on the textarea, so it works wherever the focus is inside the composer and the handler exists once. It is the way people attach a screenshot — Slack, Linear, GitHub and Notion all take one — and the only reason to reach for the paperclip instead is a file already on disk. Only a paste that carries files is taken. Pasted text is text, and a clipboard that holds both is a copied file with a name in it. Drop it in `Upload.DropZone`'s `render` to make the whole form the place files are dropped: `<Upload.DropZone render={<Upload.Composer />}>`.
Required scope
Upload.Composer must be inside <Upload.Root>.
Props
2 documented props · Native <form> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/composer.tsx
Upload.Messageinput · <textarea>
A textarea rather than an input, because Shift+Enter has to mean something.
Required scope
File.Message must be inside <File.Upload>.
Props
2 documented props · Native <textarea> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/message.tsx
Upload.Sendaction · <button>
The one submit in the composer: everything beside it is `type="button"`. → docs/rules/one-submit-per-form.md It is disabled while a file is still going up, which is the honest answer — a message sent then would carry whichever of its attachments the network had happened to finish. What says so out loud is `Upload.Uploading` beside it.
Required scope
File.Send must be inside <File.Upload>.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | — |
renderReplace the default element with your own while retaining the part’s behavior and accessibility. | ReactElement | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/send.tsx
When something is missing
- No service configuration
- Install the file-storage setup below and import "@/services". Required service roots report missing setup; gates do not replace that configuration.
- Missing capability or parent scope
- Follow each part’s context requirements and the example composition. Capability-aware controls hide when the provider cannot perform their action.