Skip to content

Theming

Every compound component accepts a styles slot map: a single prop that themes every internal element of the component. Build a slot map once, spread it where you use the component. Local style props on individual sub-components remain available as fine-grained overrides, and children render props provide the maximum-control escape hatch.

Each component exports a <Component>Styles type. Pass the map to the component’s styles prop:

sample-video.mp4
sample-video.mp4
50.0 MB
sample-video.mp4
sample-video.mp4
50.0 MB
Upload failed. Check your connection.
import { FileItem, type FileItemStyles } from "@hyperserve/video-uploader-react";
<FileItem
file={file}
styles={{
root: { background: "#1e293b", border: "1px solid #334155" },
fileName: { color: "#f1f5f9" },
fileSize: { color: "#64748b" },
statusIcon: { color: "#818cf8" },
progressFill: { background: "linear-gradient(90deg, #818cf8, #a78bfa)" },
errorMessage: { color: "#f87171" },
}}
>
<FileItem.Content />
</FileItem>

The slot map covers every internal element. See each component’s docs for the full list of slot names.

Three layers, applied in this order (last wins on conflicts):

  1. The component’s built-in defaults
  2. styles[slotName] from the slot map
  3. Individual style prop on a sub-component (when used in compound form)
// styles.fileName.color = red applies to FileName
// but the local style prop on FileItem.FileName overrides it
<FileItem
file={file}
styles={{ fileName: { color: "red" } }}
>
<FileItem.FileName style={{ color: "blue" }} /> {/* blue wins */}
</FileItem>

DropZone slots cover the default inner content (icon, primary text, browse link, supporting text) and the active drag state:

Drop videos here or browse
MP4 up to 500 MB
<DropZone
styles={{
root: { background: "#1e293b", border: "1px dashed #334155" },
activeRoot: { background: "#1e1b4b", borderColor: "#818cf8" },
primaryText: { color: "#f1f5f9" },
browseText: { color: "#818cf8" },
supportingText: { color: "#64748b" },
}}
supportingText="MP4 up to 500 MB"
/>

For full control of the inner content (replacing the icon, text, and browse link entirely), pass a children render prop instead.

Define your theme as a typed object and reuse it everywhere. This is the recommended pattern for cohesive themes across multiple components.

Drop videos here or browse
MP4 only
import {
type DropZoneStyles,
type FileItemStyles,
DropZone,
FileItem,
FileList,
} from "@hyperserve/video-uploader-react";
const darkTheme = {
dropZone: {
root: { background: "#1e293b", border: "1px dashed #334155" },
activeRoot: { borderColor: "#818cf8", borderStyle: "solid" },
primaryText: { color: "#f1f5f9" },
browseText: { color: "#818cf8" },
} satisfies DropZoneStyles,
fileItem: {
root: { background: "#1e293b", border: "1px solid #334155" },
fileName: { color: "#f1f5f9" },
fileSize: { color: "#64748b" },
statusIcon: { color: "#818cf8" },
progressFill: { background: "linear-gradient(90deg, #818cf8, #a78bfa)" },
} satisfies FileItemStyles,
};
<DropZone styles={darkTheme.dropZone} />
<FileList>
{(file) => (
<FileItem file={file} key={file.id} styles={darkTheme.fileItem}>
<FileItem.Content />
</FileItem>
)}
</FileList>

statusConfig maps every FileStatus to a background color, text color, and label. It’s separate from the slot system because it’s a per-status table, not a per-element style:

UploadingProcessingCompleteError
import { statusConfig } from "@hyperserve/video-uploader";
// statusConfig.uploading = { bg: "#eff6ff", text: "#2563eb", label: "Uploading" }

Pass overrides to StatusBadge:

<StatusBadge
status="uploading"
statusConfig={{
ready: { bg: "#dbeafe", text: "#1d4ed8", label: "Complete" },
}}
/>

Three tools, in order of granularity:

  1. styles slot map: themes everything from one prop
  2. Per-sub-component style prop: fine-grained override that wins over the slot
  3. children render prop: full custom rendering (available on DropZone, StatusBadge, Thumbnail, FileItem.StatusIcon, FileList, FileItem)

The same styles slot pattern works on React Native. Slot names are identical; types use StyleProp<ViewStyle> and StyleProp<TextStyle> instead of React.CSSProperties:

import { FileItem, type FileItemStyles } from "@hyperserve/video-uploader-react-native";
const fileItemStyles: FileItemStyles = {
root: { backgroundColor: "#1e293b", borderColor: "#334155" },
fileName: { color: "#f1f5f9" },
};
<FileItem file={file} styles={fileItemStyles}>
<FileItem.Content />
</FileItem>;
Drop videos here or browse
MP4 up to 500 MB
Drop files above to get started.