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.
Slot maps
Section titled “Slot maps”Each component exports a <Component>Styles type. Pass the map to the component’s styles prop:
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.
Merge order
Section titled “Merge order”Three layers, applied in this order (last wins on conflicts):
- The component’s built-in defaults
styles[slotName]from the slot map- Individual
styleprop 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>Theming a DropZone
Section titled “Theming a DropZone”DropZone slots cover the default inner content (icon, primary text, browse link, supporting text) and the active drag state:
<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.
Build once, spread many
Section titled “Build once, spread many”Define your theme as a typed object and reuse it everywhere. This is the recommended pattern for cohesive themes across multiple components.
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>Status config
Section titled “Status config”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:
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" }, }}/>Escape hatches
Section titled “Escape hatches”Three tools, in order of granularity:
stylesslot map: themes everything from one prop- Per-sub-component
styleprop: fine-grained override that wins over the slot childrenrender prop: full custom rendering (available onDropZone,StatusBadge,Thumbnail,FileItem.StatusIcon,FileList,FileItem)
React Native
Section titled “React Native”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>;