Reusable utility components, hooks, and functions for Hiyve video conferencing applications.
npm install @hiyve/utilities
import {
LiveClock,
TooltipIconButton,
ErrorBoundary,
useContainerBreakpoint,
} from '@hiyve/utilities';
function Header() {
const { isBelowBreakpoint, containerRef } = useContainerBreakpoint(800);
return (
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<header ref={containerRef}>
{!isBelowBreakpoint && <LiveClock />}
<TooltipIconButton tooltip="Copy" onClick={handleCopy}>
<CopyIcon />
</TooltipIconButton>
</header>
</ErrorBoundary>
);
}
Self-contained live time display that updates every second.
import { LiveClock } from '@hiyve/utilities';
<LiveClock variant="h6" />
<LiveClock
variant="body1"
updateInterval={5000}
labels={{ formatTime: (date) => date.format('HH:mm:ss') }}
colors={{ text: '#ffffff' }}
/>
| Prop | Type | Default | Description |
|---|---|---|---|
variant |
'h1' | 'h2' | ... | 'caption' |
'h5' |
Typography variant |
updateInterval |
number |
1000 |
Update interval in milliseconds |
labels |
Partial<LiveClockLabels> |
-- | Custom time formatting |
colors |
Partial<LiveClockColors> |
-- | Custom colors |
styles |
Partial<LiveClockStyles> |
-- | Custom styles (padding, textAlign) |
sx |
SxProps<Theme> |
-- | MUI sx styling |
IconButton wrapper that correctly displays tooltips on disabled buttons.
import { TooltipIconButton } from '@hiyve/utilities';
import SaveIcon from '@mui/icons-material/Save';
<TooltipIconButton
tooltip="Save changes"
disabled={!hasChanges}
onClick={handleSave}
>
<SaveIcon />
</TooltipIconButton>
| Prop | Type | Default | Description |
|---|---|---|---|
tooltip |
ReactNode |
-- | Tooltip content (required) |
placement |
TooltipProps['placement'] |
'top' |
Tooltip placement |
disabled |
boolean |
false |
Disabled state (tooltip still works) |
styles |
Partial<TooltipIconButtonStyles> |
-- | Custom styles |
tooltipProps |
Omit<TooltipProps, 'title' | 'children' | 'placement'> |
-- | Additional Tooltip props |
children |
ReactNode |
-- | Icon element |
Extends all standard MUI IconButtonProps (except title).
React error boundary that catches render errors in its child tree.
import { ErrorBoundary } from '@hiyve/utilities';
// Static fallback
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<MyComponent />
</ErrorBoundary>
// Render function fallback with error details and reset
<ErrorBoundary
fallback={(error, reset) => (
<div>
<p>Error: {error.message}</p>
<button onClick={reset}>Try Again</button>
</div>
)}
onError={(error, errorInfo) => logToService(error)}
>
<VideoGrid />
</ErrorBoundary>
| Prop | Type | Description |
|---|---|---|
children |
ReactNode |
Content to render when no error has occurred |
fallback |
ReactNode | (error: Error, reset: () => void) => ReactNode |
Content to render when an error is caught |
onError |
(error: Error, errorInfo: ErrorInfo) => void |
Callback invoked when an error is caught |
When fallback is a function, it receives the caught Error and a reset function that clears the error state and re-renders the children.
Responsive container detection using ResizeObserver. Returns whether the container is below a given width threshold.
import { useContainerBreakpoint } from '@hiyve/utilities';
function ResponsivePanel() {
const { isBelowBreakpoint, containerRef } = useContainerBreakpoint(600);
return (
<div ref={containerRef}>
{isBelowBreakpoint ? <CompactView /> : <FullView />}
</div>
);
}
Signature: useContainerBreakpoint(breakpoint: number, options?: UseContainerBreakpointOptions)
Returns: UseContainerBreakpointResult
| Return Field | Type | Description |
|---|---|---|
isBelowBreakpoint |
boolean |
Whether the container width is below the breakpoint |
containerRef |
RefObject<HTMLElement> |
Ref to attach to the container element |
| Option | Type | Default | Description |
|---|---|---|---|
initialValue |
boolean |
false |
Initial value before the first measurement |
Delays updating a value until a specified time has passed since the last change. Useful for search inputs and API call throttling.
import { useDebounce } from '@hiyve/utilities';
function SearchComponent() {
const [searchInput, setSearchInput] = useState('');
const debouncedSearch = useDebounce(searchInput, 300);
useEffect(() => {
performSearch(debouncedSearch);
}, [debouncedSearch]);
return (
<input
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
/>
);
}
Signature: useDebounce<T>(value: T, delay: number): T
| Parameter | Type | Description |
|---|---|---|
value |
T |
The value to debounce |
delay |
number |
Debounce delay in milliseconds |
Returns the debounced value.
Form validation hook with built-in validator factories.
import { useValidation, validators } from '@hiyve/utilities';
function PollQuestionInput() {
const [question, setQuestion] = useState('');
const { error, validate, isValid, clearError } = useValidation(question, [
validators.required('Question is required'),
validators.maxLength(500, 'Question must be 500 characters or less'),
]);
const handleSubmit = () => {
if (validate()) {
submitPoll(question);
}
};
return (
<TextField
value={question}
onChange={(e) => setQuestion(e.target.value)}
error={!!error}
helperText={error}
/>
);
}
Signature: useValidation<T>(value: T, validators: Validator<T>[]): UseValidationResult
| Return Field | Type | Description |
|---|---|---|
error |
string | null |
Current error message, or null if valid |
validate |
() => boolean |
Run validators and update error state; returns true if valid |
isValid |
boolean |
Whether the value is currently valid |
clearError |
() => void |
Clear the current error |
setError |
(message: string | null) => void |
Set a custom error message |
The validators object provides factory functions that return Validator functions:
| Validator | Signature | Description |
|---|---|---|
validators.required |
(message?: string) |
Value must be a non-empty string |
validators.minLength |
(min: number, message?: string) |
Minimum string length |
validators.maxLength |
(max: number, message?: string) |
Maximum string length |
validators.pattern |
(regex: RegExp, message: string) |
Value must match a regex pattern |
validators.noSpecialChars |
(message?: string) |
Only letters, numbers, spaces, hyphens, underscores |
validators.range |
(min: number, max: number, message?: string) |
Numeric value within a range |
validators.email |
(message?: string) |
Valid email address format |
validators.url |
(message?: string) |
Valid URL format |
validators.custom |
<T>(fn: (value: T) => boolean, message: string) |
Custom validation logic |
Persists state to localStorage with the same API as useState. Values survive page reloads and are automatically synchronized across browser tabs.
import { usePersistedState } from '@hiyve/utilities';
function JoinForm() {
const [userName, setUserName] = usePersistedState('hiyve-userName', '');
const [roomName, setRoomName] = usePersistedState('hiyve-roomName', '');
return (
<>
<input value={userName} onChange={(e) => setUserName(e.target.value)} />
<input value={roomName} onChange={(e) => setRoomName(e.target.value)} />
</>
);
}
Signature: usePersistedState<T>(key: string, defaultValue: T): [T, (value: T | ((prev: T) => T)) => void]
| Parameter | Type | Description |
|---|---|---|
key |
string |
The localStorage key to persist under |
defaultValue |
T |
The initial value when no stored value exists |
Returns a [value, setValue] tuple identical to useState. The setter supports both direct values and functional updates. Works in SSR environments by falling back to the default value when window is unavailable. Corrupted or unparseable stored values fall back to the default gracefully.
Creates a throttled version of a function that executes at most once every wait milliseconds. The first call executes immediately; subsequent calls within the wait period are queued.
import { throttle } from '@hiyve/utilities';
const throttledUpdate = throttle((data) => {
broadcastUpdate(data);
}, 50);
canvas.on('object:modified', () => throttledUpdate(canvas.toJSON()));
// Clean up
throttledUpdate.cancel();
Signature: throttle<T>(fn: T, wait: number): T & { cancel: () => void }
Returns a throttled function with a cancel() method to clear any pending execution.
Functions for generating consistent user display elements (initials and colors).
import { getInitials, getUserColor, DEFAULT_AVATAR_PALETTE } from '@hiyve/utilities';
getInitials('John Doe'); // 'JD'
getInitials('jane_smith'); // 'JS'
getInitials('admin'); // 'AD'
getInitials(''); // '?'
getUserColor('user-123'); // consistent color from default palette
getUserColor('user-123', ['#f00', '#0f0', '#00f']); // consistent color from custom palette
| Export | Signature | Description |
|---|---|---|
getInitials |
(name: string) => string |
Extracts 1-2 uppercase initials from a name. Splits on spaces, hyphens, and underscores. Returns '?' for empty strings. |
getUserColor |
(userId: string | undefined, palette?: string[]) => string |
Returns a deterministic color for a given user ID. The same ID always produces the same color. |
DEFAULT_AVATAR_PALETTE |
string[] |
Array of 12 distinct colors designed for participant differentiation. |
Async retry with exponential backoff, jitter, and customizable retry conditions.
import { withRetry, isNetworkErrorRetryable, sleep } from '@hiyve/utilities';
// Basic retry
const data = await withRetry(() => fetchData('/api/data'), {
maxRetries: 3,
});
// Custom retry logic
const result = await withRetry(() => uploadFile(file), {
maxRetries: 5,
initialDelay: 500,
isRetryable: isNetworkErrorRetryable,
onRetry: (attempt, error, delay) => {
console.log(`Retry ${attempt} after ${delay}ms`);
},
});
// Standalone delay
await sleep(2000);
| Export | Signature | Description |
|---|---|---|
withRetry |
<T>(fn: () => Promise<T>, options?: RetryOptions) => Promise<T> |
Execute an async function with exponential backoff retry. Throws the last error if all retries are exhausted. |
calculateBackoffDelay |
(attempt: number, options?) => number |
Calculate the delay in milliseconds for a given attempt (0-indexed). |
sleep |
(ms: number) => Promise<void> |
Promise-based delay. |
isNetworkErrorRetryable |
(error: unknown) => boolean |
Returns true for transient network errors (fetch failures, HTTP 5xx, 429, 408). |
DEFAULT_RETRY_OPTIONS |
object |
Default configuration: { maxRetries: 3, initialDelay: 1000, maxDelay: 10000, backoffMultiplier: 2, jitter: true } |
RetryOptions:
| Option | Type | Default | Description |
|---|---|---|---|
maxRetries |
number |
3 |
Maximum number of retry attempts |
initialDelay |
number |
1000 |
Initial delay in milliseconds |
maxDelay |
number |
10000 |
Maximum delay in milliseconds |
backoffMultiplier |
number |
2 |
Multiplier for exponential backoff |
jitter |
boolean |
true |
Add randomized jitter to prevent thundering herd |
isRetryable |
(error: unknown) => boolean |
all errors | Determine if an error should be retried |
onRetry |
(attempt, error, nextDelay) => void |
-- | Callback invoked before each retry |
Namespaced debug logging with level filtering and global configuration.
import { createDebugLogger, configureDebug, isDebugEnabled } from '@hiyve/utilities';
// Enable debug logging at startup
configureDebug({ enabled: true });
// Create a scoped logger
const debug = createDebugLogger('hiyve:my-component');
debug.log('Component mounted');
debug.warn('Deprecated prop used');
debug.error('Failed to load data', error);
// Check enabled state
if (debug.enabled) {
debug.log('Detailed info:', expensiveComputation());
}
| Export | Signature | Description |
|---|---|---|
createDebugLogger |
(namespace: string) => DebugLogger |
Create a namespaced logger. Messages are prefixed with [namespace]. |
configureDebug |
(config: DebugConfig) => void |
Configure global debug settings (enabled, filter, minLevel). |
isDebugEnabled |
() => boolean |
Check if debug logging is globally enabled. |
noopLogger |
DebugLogger |
Logger instance that does nothing. Useful as a default or fallback. |
hiyveDebug |
DebugLogger |
Pre-configured logger with the 'hiyve' namespace. |
DebugLogger interface:
| Method/Property | Type | Description |
|---|---|---|
log |
(...args: unknown[]) => void |
Log a standard message |
warn |
(...args: unknown[]) => void |
Log a warning |
error |
(...args: unknown[]) => void |
Log an error |
debug |
(...args: unknown[]) => void |
Log a debug-level message |
enabled |
boolean (readonly) |
Whether this logger is currently active |
DebugConfig options:
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
false in production |
Enable or disable debug logging globally |
filter |
string | RegExp |
-- | Namespace filter (e.g. 'hiyve:*' or `/^hiyve:(client |
minLevel |
DebugLevel |
'log' |
Minimum log level: 'debug', 'log', 'warn', or 'error' |
Maps raw error messages to user-friendly strings using case-insensitive substring matching. Useful in onError callbacks to display clear, actionable messages to end users.
import { formatHiyveError } from '@hiyve/utilities';
<HiyveProvider
onError={(err) => setError(formatHiyveError(err.message || String(err)))}
>
With custom messages that extend the defaults:
import { formatHiyveError, DEFAULT_HIYVE_ERROR_MESSAGES } from '@hiyve/utilities';
const friendly = formatHiyveError(error, {
'rate limit': 'Too many requests. Please wait a moment and try again.',
});
| Export | Signature | Description |
|---|---|---|
formatHiyveError |
(error: string | Error, customMessages?: Record<string, string>) => string |
Returns a user-friendly message if the error matches a known pattern, or the original message if no match is found. Accepts a raw string or an Error object. Optional customMessages extend and override the defaults. |
DEFAULT_HIYVE_ERROR_MESSAGES |
Record<string, string> |
Built-in mapping of error substrings to friendly messages. Covers common room-not-found, expired token/session, and incorrect password scenarios. Spread into a new object to extend with your own mappings. |
Default mappings included:
| Error substring | Friendly message |
|---|---|
'does not exist', 'not found', 'no room' |
Unable to join room. The room name may be incorrect or the host hasn't started the meeting yet. |
'token' |
Your invite link has expired or is invalid. Please request a new one. |
'expired' |
Your session has expired. Please rejoin the room. |
'password' |
Incorrect password. Please try again. |
Framework-agnostic types and layout algorithms shared between @hiyve/video-tile, @hiyve/video-grid, and their Angular counterparts. These are the single source of truth — the framework-specific packages re-export them for backward compatibility.
import {
type OverlayPosition,
type TilePosition,
type MuteStatus,
type MoodType,
type MoodData,
type BuiltInLayoutMode,
type LayoutMode,
getGridColumns,
getGridClass,
getPositionStyles,
calculateSpeakerLayout,
calculateSidebarLayout,
} from '@hiyve/utilities';
Angular packages should import from the @hiyve/utilities/video subpath. This entry point is a standalone bundle (~2KB) with zero React or MUI dependencies:
import { type MuteStatus, getGridColumns } from '@hiyve/utilities/video';
| Type | Description |
|---|---|
OverlayPosition |
'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' |
TilePosition |
{ left: number; top: number; width: number; height: number } |
MuteStatus |
{ audio: boolean; video: boolean; output?: boolean } |
MoodType |
'neutral' | 'happy' | 'sad' | 'angry' | 'fearful' | 'disgusted' | 'surprised' |
MoodData |
{ mood: MoodType; confidence?: number; engagement?: number; timestamp?: number } |
BuiltInLayoutMode |
'grid' | 'speaker' | 'sidebar' |
LayoutMode |
string (any layout name, including custom layouts) |
| Export | Signature | Description |
|---|---|---|
getGridColumns |
(count: number) => number |
Number of grid columns for a participant count |
getGridClass |
(count: number) => string |
CSS class name for grid styling (e.g. 'grid-2x2') |
getPositionStyles |
(position: OverlayPosition) => Record<string, string> |
CSS properties for positioning an overlay element |
calculateSpeakerLayout |
(params) => Record<string, TilePosition> |
Compute tile positions for speaker layout (dominant + filmstrip) |
calculateSidebarLayout |
(params) => Record<string, TilePosition> |
Compute tile positions for sidebar layout (dominant + side panel) |
All TypeScript interfaces and types are exported for use in custom implementations.
| Type | Description |
|---|---|
LiveClockProps |
Props for the LiveClock component |
LiveClockLabels |
Customizable time format labels |
LiveClockStyles |
Style configuration (padding, textAlign) |
LiveClockColors |
Color configuration (text) |
TooltipIconButtonProps |
Props for TooltipIconButton (extends IconButtonProps) |
TooltipIconButtonStyles |
Style configuration (wrapperDisplay) |
ErrorBoundaryProps |
Props for ErrorBoundary |
UseContainerBreakpointResult |
Return type of useContainerBreakpoint |
UseContainerBreakpointOptions |
Options for useContainerBreakpoint |
Validator<T> |
Validator function type: (value: T) => string | null |
UseValidationResult |
Return type of useValidation |
UseThrottleOptions |
Options type with leading and trailing flags |
RetryOptions |
Options for withRetry |
DebugLevel |
'log' | 'warn' | 'error' | 'debug' |
DebugLogger |
Debug logger interface |
DebugConfig |
Configuration for configureDebug |
OverlayPosition |
Video tile overlay corner position |
TilePosition |
Absolute position and size for a video tile |
MuteStatus |
Audio/video/output mute state |
MoodType |
7-emotion sentiment category |
MoodData |
Mood analysis result with confidence and engagement |
BuiltInLayoutMode |
Built-in layout mode union |
LayoutMode |
Layout mode (any string, including custom) |
Default configuration objects and merge helpers for component customization.
| Export | Description |
|---|---|
defaultLiveClockLabels |
Default time format: 'MMMM D, YYYY h:mm:ss A' |
defaultLiveClockStyles |
Default styles: { padding: 2, textAlign: 'center' } |
defaultLiveClockColors |
Default colors: { text: 'inherit' } |
defaultTooltipIconButtonStyles |
Default styles: { wrapperDisplay: 'inline-flex' } |
mergeLiveClockLabels |
(partial?) => LiveClockLabels -- merge partial labels with defaults |
mergeLiveClockStyles |
(partial?) => LiveClockStyles -- merge partial styles with defaults |
mergeLiveClockColors |
(partial?) => LiveClockColors -- merge partial colors with defaults |
mergeTooltipIconButtonStyles |
(partial?) => TooltipIconButtonStyles -- merge partial styles with defaults |
import { defaultLiveClockLabels, LiveClock } from '@hiyve/utilities';
// Override only the time format, keeping other defaults
<LiveClock
labels={{
...defaultLiveClockLabels,
formatTime: (date) => date.format('HH:mm:ss'),
}}
/>
@hiyve/utilities/video entry point with no React/MUI dependenciesPeer dependencies (for component exports):
react ^18.0.0@mui/material ^5.0.0 || ^6.0.0@mui/icons-material ^5.0.0 || ^6.0.0@emotion/react ^11.0.0@emotion/styled ^11.0.0Non-component exports (hooks, utilities, avatar, debug, retry, throttle) have no peer dependency requirements beyond React.
The @hiyve/utilities/video subpath (video types and layout algorithms) has no peer dependencies at all and can be used from Angular or any other framework.
Proprietary - Hiyve SDK