Theming and labels
Rebrand through CSS variables alone, and replace every user-facing string — including the ones the components do not render.
Labels — every string is replaceable
All user-facing copy flows through one object, UploaderLabels. English is the
default; Spanish ships as ES_LABELS. Select a language once at the root:
import { UploaderProvider } from 'uploaderkit/react'
;<UploaderProvider language='es'>
<App />
</UploaderProvider>Any partial override wins over that base, per component or per hook:
<Uploader {...props} labels={{ dropPrompt: 'Drop your invoice here' }} />The copy reaches further than the components
The object is not only for markup — it words the validation messages and the server's HTTP responses too, which is what keeps one rejection from arriving in two languages:
import { ES_LABELS, validateForScope } from 'uploaderkit'
const result = await validateForScope(scopes, 'invoices', file, ES_LABELS)
// result.message is Spanish| Surface | How it takes the copy |
|---|---|
Uploader, SlottedUploader, presets | labels prop, over the provider |
useUploader, useSlottedUploader | labels option, over the provider |
validateFile, validateFiles | labels in ValidationOptions |
validateForScope | 4th argument |
createXhrUploadStrategy, the view resolvers | labels option |
createStorage — and both framework adapters | labels option, once |
On the server, one option covers the whole round trip: createStorage resolves
the copy and republishes it as storage.labels, which is exactly where
createExpressStorageHandlers and createNextStorageHandlers read from, so a
route can never answer in a different language than the service behind it.
import { ES_LABELS } from 'uploaderkit'
const storage = createStorage({ scopes, provider, labels: ES_LABELS })
// 401 → "No autorizado"; a rejected upload → the same 422 text the browser showedScopeError is the one exception, deliberately: it flags a wiring bug, stays
English, and is never serialized to a client.
See UploaderLabels for the full key list.
Theming
The styled layer reads --color-ui-* / --radius-ui* CSS variables, declared
with defaults in tailwind.css. An app rebrands the whole styled layer with
a single override:
:root {
--color-ui-primary: #c41e3a;
--color-ui-primary-hover: #8b1529;
--radius-ui: 0.25rem;
}The override scopes like any CSS variable: put it on a wrapper div to
re-brand a single uploader instead of the whole app.
Motion ships with the components: rows animate in
(--animate-ui-fade-in), the viewer and the confirm dialog fade/scale on
enter and exit, the drag state lifts the zone and a press compresses it, and
the slot indicator pulses while uploading. All CSS — nothing to configure,
prefers-reduced-motion friendly to override from the app.
Dropzone, FileItem and FileViewer are exported separately for building a
different arrangement out of the same pieces.