Headless / Files & documents
Organize files
Compose file actions & trash with storage permissions, loading and failure states.
42 public parts · 1examples
@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.
Files.Container · 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"import "@/services"import { Files } from "@slices.sh/file-storage/headless"export function Example() { return ( <Files.Root> <Files.Entries> <Files.Entry> <Files.SelectEntry /> <Files.EntryName /> <Files.Download>Download</Files.Download> <Files.DeleteEntry>Delete</Files.DeleteEntry> </Files.Entry> </Files.Entries> <Files.Confirming> <Files.ConfirmDelete>Confirm deletion</Files.ConfirmDelete> <Files.CancelDelete>Cancel</Files.CancelDelete> </Files.Confirming> <Files.TrashNotice> <Files.Undo>Undo</Files.Undo> </Files.TrashNotice> </Files.Root> )}Configure a file-storage provider. Mutating actions follow the storage rules and current user permissions.
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.
Files.KeepSelectionscope
The selection stays among the rows on screen: a filter, a search or another folder that takes a selected row away takes it out of the selection too. The rows of a tree and the columns of a Miller view count as on screen: what was read under the open folder and still passes the filters. Otherwise the file picked in the third column would be let go of the moment it was picked. A part of its own, not something every browser does, because a browser that keeps a selection across folders (to move it, to send it) wants the rows it cannot see.
Required scope
Files.KeepSelection must be inside <Files.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/keep-selection.tsx
Files.WhenSelectedgate
Something is selected. `count` is how many, for the line that says so and the actions on them.
Required scope
Files.WhenSelected must be inside <Files.Root>.
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/when-selected.tsx
Files.Statuscontent · <div>
The line under the listing: how many there are and what is picked. Counts the entries of the folder, not the rows on screen, which a tree makes more by every branch it opens: "12 items" is what the folder holds. Not a live region, because the announcer already says what the listing is showing, and two of them said the same thing twice.
Required scope
Files.Status must be inside <Files.Root>.
Props
2 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: StatusState) => 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/status.tsx
Files.SelectEntryinput · <input>
The tick beside one row. Its accessible name is the file's, because a list of them all called "Select" reads as "Select, checkbox, unchecked" twenty times over with nothing to tell one row from another. A pattern may still name it something else; nothing here is a word anybody wrote. Shift extends from the last row touched, over the rows actually on screen — which is why this reads the listing rather than asking the selection to remember it. Whether Shift was held is a fact about the pointer, and a `change` event does not carry one, so the click that precedes it leaves the answer here for it.
Required scope
File.SelectEntry must be inside <File.Entries>.
File.SelectEntry must be inside <File.Browser>.
Props
2 documented props · Native <input> 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/select-entry.tsx
Files.SelectAllinput · <input>
Everything on screen, ticked or cleared at once. Over the rows that are actually shown rather than over the folder: a bucket is longer than a page, and a control that claimed to select what has not been read would be selecting things nobody could then see it had selected. → docs/rules/capability-honesty.md Half a selection is `indeterminate`, which a native checkbox has as a property and not as an attribute, so it is written to the element rather than rendered. It was an `aria-checked="mixed"` standing in for it before, which is the statement without the fact: a browser computes this control's state from the property, and half a selection reading as "unchecked" is the half that gets cleared by accident. Nothing at all where only one row may be selected: there is no "all" to offer.
Required scope
Files.SelectAll must be inside <Files.Root>.
Props
2 documented props · Native <input> 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/select-all.tsx
Files.DeleteSelectedaction · <button>
Takes everything that is selected out in one go, and one Undo puts all of it back. Only where the backend has a trash that can give it back: deleting one entry for good asks first, in its row, and there is no row to ask in for several. Nothing is drawn where there is none, which is honest about what a backend can do rather than a button that asks nothing. → docs/rules/capability-honesty.md
Required scope
Files.DeleteSelected must be inside <Files.Root>.
Files.DeleteSelected requires Files.Root.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { count: 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/delete-selected.tsx
Files.ClearSelectionaction · <button>
Lets go of everything that is selected.
Required scope
Files.ClearSelection must be inside <Files.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-selection.tsx
Files.ContextAreacontent · <li>
A right-click menu per row: the area, the popup, items and links.
Required scope
Files.ContextArea requires Files.ContextMenu.
Props
2 documented props · Native <li> 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/context-area.tsx
Files.ContextMenuscope
A right-click menu per row: the area, the popup, items and links.
Required scope
Files.ContextMenu requires Files.Root.
Files.ContextMenu requires Files.Entries.
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/context-menu.tsx
Files.ContextPopupcontent · <div>
A right-click menu per row: the area, the popup, items and links.
Required scope
Files.ContextPopup requires Files.ContextMenu.
Props
3 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
popupThe menu popup and its positioner, which the pattern styles. | ReactElement<ComponentProps<typeof MotionMenu>> | — |
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/context-popup.tsx
Files.ContextItemaction · <button>
A right-click menu per row: the area, the popup, items and links.
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/context-item.tsx
Files.ContextLinkcontent · <a>
A right-click menu per row: the area, the popup, items and links.
Props
2 documented props · Native <a> 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/context-link.tsx
Files.Renamescope
The rename scope of a row, gates for renaming or not, the trigger, the form with its input, save, cancel and the refusal.
Required scope
Files.Rename requires Files.Entries.
Files.Rename requires Files.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/rename.tsx
Files.Renaminggate
The rename scope of a row, gates for renaming or not, the trigger, the form with its input, save, cancel and the refusal.
Required scope
Files.Renaming requires Files.Rename.
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/renaming.tsx
Files.RenameFormaction · <form>
Renames the row it is in, in place. Submitting the old name does nothing.
Required scope
Files.RenameForm must be inside <Files.Entries>.
Files.RenameForm must be inside <Files.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/rename-form.tsx
Files.RenameInputinput · <input>
The new name, starting as the old one. `name="rename"` is what the form reads.
Required scope
Files.RenameInput must be inside <Files.Entries>.
Props
2 documented props · Native <input> 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/rename-input.tsx
Files.RenameSubmitaction · <button>
The rename scope of a row, gates for renaming or not, the trigger, the form with its input, save, cancel and the refusal.
Required scope
Files.RenameSubmit requires Files.Rename.
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/rename-submit.tsx
Files.RenameCancelaction · <button>
The rename scope of a row, gates for renaming or not, the trigger, the form with its input, save, cancel and the refusal.
Required scope
Files.RenameCancel requires Files.Rename.
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/rename-cancel.tsx
Files.NotRenaminggate
The rename scope of a row, gates for renaming or not, the trigger, the form with its input, save, cancel and the refusal.
Required scope
Files.NotRenaming requires Files.Rename.
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/not-renaming.tsx
Files.RenameTriggeraction · <button>
The rename scope of a row, gates for renaming or not, the trigger, the form with its input, save, cancel and the refusal.
Required scope
Files.RenameTrigger requires Files.Entries.
Files.RenameTrigger requires Files.Rename.
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/rename-trigger.tsx
Files.RenameFailedgate
The rename scope of a row, gates for renaming or not, the trigger, the form with its input, save, cancel and the refusal.
Required scope
Files.RenameFailed requires Files.Rename.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { error: FileErrorSlug }) => ReactNode) | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/rename-failed.tsx
Files.NewFolderaction · <button>
Makes a folder in the open one, and opens its name for typing, as a file manager does. The folder is made at once under `folderName`, or the next free name beside one that is there, and its row takes the focus with the name selected: Enter keeps it, Escape keeps what it was called. Nothing is asked first, because a form for a name is a step between wanting a folder and having one, and the row is where the name was going to be typed anyway.
Required scope
Files.NewFolder must be inside <Files.Root>.
Props
3 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
folderNameRequired | string | — |
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/new-folder.tsx
Files.NewFolderFormaction · <form>
The form that makes a folder. A form rather than a button with a prompt, because the name needs a label, an error and a keyboard — and `window.prompt` has none of those. The name is read from the form on submit rather than tracked on every keystroke, so the input stays uncontrolled and a skin may put anything at all around it.
Required scope
Files.NewFolderForm must be inside <Files.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/new-folder-form.tsx
Files.NewFolderNameinput · <input>
The new folder's name. `name="folder"` is what the form reads.
Props
2 documented props · Native <input> 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/new-folder-name.tsx
Files.NewFolderSubmitaction · <button>
Make a folder at once and type its name in its row, or make one from a name typed first.
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/new-folder-submit.tsx
Files.Confirminggate
This row is the one that has asked to be deleted: where the answer goes. Escape calls it off, which is what everybody presses at a question they did not mean to ask. `useEscape` watches for it only while something is armed, so there is no key being watched for the rest of the time.
Required scope
Files.Confirming must be inside <Files.Entries>.
Files.Confirming must be inside <Files.Root>.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { name: string; kind: "file" | "directory" }) => ReactNode) | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/confirming.tsx
Files.CancelDeleteaction · <button>
Calls the deletion off. The other half of the question, and the one Escape presses.
Required scope
Files.CancelDelete must be inside <Files.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/cancel-delete.tsx
Files.ConfirmDeleteaction · <button>
Deletes the row that asked, and everything under it when it is a folder. It takes the focus when it appears, because the button that was pressed to get here has just stopped existing: focus would otherwise fall back to the document and a keyboard would have to walk the whole row again to answer a question it just asked. → https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/
Required scope
Files.ConfirmDelete must be inside <Files.Confirming>.
Files.ConfirmDelete must be inside <Files.Root>.
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/confirm-delete.tsx
Files.DeleteEntryaction · <button>
Uses reversible trash when the backend confirms it; otherwise asks before permanent deletion.
Required scope
File.DeleteEntry must be inside <File.Entries>.
File.DeleteEntry must be inside <Files.Root>.
Files.DeleteEntry requires Files.Root.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { name: string; kind: "file" | "directory" }) => 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/delete-entry.tsx
Files.TrashEntriesgate
The notice for the file taken out most recently, for `undoSeconds` after it was. Only the latest, because a notice per removal stacks above the list until the list is pushed off the screen. It is the notice that goes, never the receipt: Ctrl/Cmd+Z and `Files.RefreshTrash` still reach every receipt the backend holds. A receipt that was already old when this screen first had it, because it was loaded and not just made, never shows. A number that is not finite keeps the notice there until it is undone, and so does a partial one, which is a warning that something is still in place rather than a courtesy.
Capability: trash
Required scope
Files.TrashEntries requires Files.Root.
Props
2 documented props
| Prop | Type | Default |
|---|---|---|
undoSeconds | number | 5 |
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/trash-entries.tsx
Files.TrashNoticecontent · <div>
The latest receipt for undoSeconds, its notice with { name, partial, count }, Undo, the refusal with its error slug, and reloading receipts after a lost response. All render nothing where the provider has no trash.
Capability: trash
Required scope
Files.TrashNotice requires Files.TrashEntries.
Props
2 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { name: string; partial: boolean; count: 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/trash-notice.tsx
Files.Undoaction · <button>
The latest receipt for undoSeconds, its notice with { name, partial, count }, Undo, the refusal with its error slug, and reloading receipts after a lost response. All render nothing where the provider has no trash.
Capability: trash
Required scope
Files.Undo requires Files.Root.
Files.Undo requires Files.TrashEntries.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { name: string; pending: 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/undo.tsx
Files.TrashFailedgate
The latest receipt for undoSeconds, its notice with { name, partial, count }, Undo, the refusal with its error slug, and reloading receipts after a lost response. All render nothing where the provider has no trash.
Capability: trash
Required scope
Files.TrashFailed requires Files.Root.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: { error: FileErrorSlug }) => ReactNode) | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/trash-failed.tsx
Files.RefreshTrashaction · <button>
The latest receipt for undoSeconds, its notice with { name, partial, count }, Undo, the refusal with its error slug, and reloading receipts after a lost response. All render nothing where the provider has no trash.
Capability: trash
Required scope
Files.RefreshTrash requires Files.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/refresh-trash.tsx
Files.Clashgate
A name is taken where something was going, and somebody has to say what happens. One gate for the two ways it happens, a file on its way in and an entry being moved, since to the person they are one question: it is there while any is waiting and says the first. The answers are `Files.ClashReplace`, `Files.ClashKeepBoth` and `Files.ClashSkip`, for this one or, with `Files.ClashAll` ticked, for every one waiting and the ones of this batch still to come. Escape is skip, as it is the answer that leaves everything as it was.
Props
1 documented props
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: ClashSaid) => ReactNode) | — |
Required props are marked. A dash means no explicit default is declared in this component.
Source: @slices.sh/file-storage/src/headless/clash.tsx
Files.ClashReplaceaction · <button>
Writes the new one over the one that is there.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: ClashSaid) => 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/clash-replace.tsx
Files.ClashKeepBothaction · <button>
Gives the new one the next free name beside the one that is there. The answer that loses nothing.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: ClashSaid) => 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/clash-keep-both.tsx
Files.ClashSkipaction · <button>
Leaves the new one out and the one that is there as it was.
Props
2 documented props · Native <button> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: ClashSaid) => 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/clash-skip.tsx
Files.ClashMoregate
More may follow this question, so "apply to all" has something to apply to. Around the words and the box that offer it.
Required scope
Files.ClashMore must be inside <Files.Clash>.
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/clash-more.tsx
Files.ClashAllinput · <input>
"Apply to all": the answer given next is the answer for every name that is taken in this batch. Put inside `Files.ClashMore`, which is where there is something to apply it to.
Required scope
Files.ClashAll must be inside <Files.Clash>.
Props
2 documented props · Native <input> 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/clash-all.tsx
Files.DropIntocontent · <div>
An entry that can be picked up, and a folder that can be dropped into. One part for both, because on screen they are one gesture and every row is capable of each: a file is carried and a folder receives, and a folder is carried too. Splitting them would leave a row choosing between two parts by its own kind, which is the conditional that gates exist to remove. → docs/rules/states-gate-themselves.md Keyboard as well as pointer, on the same reasoning as the board's `Collection.Row`: Enter or Space picks a row up and puts it down, Escape drops it where it was. A drag that is only a drag is a feature that nobody without a mouse has. Which is why focus marks a target as well as the pointer does. `dragenter` fires for a pointer alone, so a row carried by a keyboard used to travel over folder after folder with none of them lit and nothing said — the gesture worked and gave no sign that it would. `Files.CarryAnnouncer` is the other half of that. Which drops are refused is `canReceive`, beside the hook, because it is path grammar and a part reaches a service for types alone. Files from outside the page are the other drop on a folder's row: where there is an upload queue, the files and folders dropped on a folder go into it, as they do into the open folder when dropped on the listing — through the same limits, and into the same queue, so what is on its way is drawn where it is going. `move={false}` leaves the carrying out for a screen that may upload and may not change what is there. A row that is itself an upload on its way is neither carried nor dropped on. `data-drop` says a folder is about to receive something, `data-invalid` that what is over it is nothing it takes; `data-active` says this row is the one in the air.
Required scope
Files.DropInto must be inside <Files.Entries>.
Files.DropInto must be inside <Files.Root>.
Props
3 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
move | boolean | true |
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/drop-into.tsx
Files.CarryAnnouncercontent · <div>
What is in the air, for somebody who cannot see it. `Files.DropInto` can be worked with a keyboard — Enter picks a row up, Enter on a folder puts it down, Escape leaves it where it was — and every part of that was silent: a row lifted with no pointer said nothing, and the folder it was over was a ring nobody could see. A gesture whose whole feedback is a highlight is a gesture only a sighted mouse has. Its own region rather than a line in `Files.Announcer`, because a carry and a listing are two different things happening to the same rows and one of them runs while the other is being read out. The words are the pattern's, and so is whether to say them at all: a configuration with nothing to move never renders this. → docs/rules/words-in-copy-looks-in-skin.md
Required scope
Files.CarryAnnouncer must be inside <Files.Root>.
Props
2 documented props · Native <div> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: CarrySaid) => 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/carry-announcer.tsx
Files.Containerscope
The one place a file explorer talks to the file service. It maps the client onto what the screen below draws: the folder that is open and its pages (`Files.Root`), the folders read besides it, from the store's listings or from `loadChildren`, and the address of every file and every picture, which the parts ask the client for and the server signs. What the screen may do is read here too, from what the client has: a part that needs `upload` asks `Files.CanUpload`, so a service with none gets a browser with no upload button and no drop, and not a button that fails when pressed. The screen never calls the service itself: pattern code calls no hook, and every part under here reaches the service through this container's contexts. → docs/rules/patterns-read-no-state.md
Props
2 documented props
| Prop | Type | Default |
|---|---|---|
loadChildrenWhere a folder's children come from when it is not the store's own listing. | LoadChildren | — |
childrenContent rendered inside this part. | ReactNode | — |
Required props are marked. A dash means no explicit default is declared in this component.
Also inherits Omit<FilesRootProps, "children">. See TypeScript for the complete declaration.
Source: @slices.sh/file-storage/src/headless/container.tsx
Files.Rootscope
The browser's root, and Files.Root with Files.Children in one, the seam that reads what the config's client can do.
Props
9 documented props
| Prop | Type | Default |
|---|---|---|
configThe file-storage config, a slug from the registry ( | string | — |
enabledLeft out or | Enabled | — |
bucket | string | — |
initialPathWhere the browser opens. Give | string | "/" |
path | string | — |
multiple | boolean | true |
searchLeavesWithFolderOpening another folder ends a search, as in Finder, instead of carrying it into the folder. | boolean | false |
ifExistsWhat moving an entry into a folder that has one of that name does. Left out, it is
refused. | 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/files-root.tsx
Files.Listedgate
The folder is being listed, or has been: where its headings and its rows go. Everything a listing is made of belongs inside this — the column headings, the skeletons that wait for the rows, the rows, the empty state that stands in for them, the page after this one — so that they share one place in the layout. A folder with nothing in it then keeps its headings over a body that says so, rather than a sentence above headings with nothing under them. Nothing when the read failed, where `Files.Failed` says why and headings over no rows would say nothing, nor where no backend is installed, where `Files.Unavailable` is the answer.
Required scope
Files.Listed must be inside <Files.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/listed.tsx
Files.Entriesscope
What the browser is showing: the directory's children, or what the search found. Under a folder that is open, in a tree, its entries follow it with how far in they are. One part for both, because a screen that drew them separately would be two lists whose rows, selection and actions drifted apart — and a search that showed a different kind of row than the folder it searched is the thing people report as a bug. Where the screen draws uploads in place, each one that is on its way is a row among these, in the order of the rest, with `Files.Pending` to say how far it has got. A row that is not there yet cannot be opened, picked, renamed or moved: its parts say so themselves.
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/entries.tsx
Files.Entrycontent · <li>
The gate around everything a listed folder draws, the repeater of rows on screen, the row, the next page, roving keys over the rows, a windowed list for long folders and the live announcement of what the folder now holds.
Required scope
Files.Entry must be inside <Files.Entries>.
Files.Entry must be inside <Files.Root>.
Props
2 documented props · Native <li> attributes supported
| Prop | Type | Default |
|---|---|---|
childrenContent rendered inside this part. | ReactNode | ((state: EntryState) => 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/entry.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.