2026-01-11 16:01:27 +00:00
# Error Display Strategy
**Date**: 2025-01-27
**Action**: 3.1.1.5 - Create error display strategy document
**Status**: ✅ Complete
## Overview
This document defines the strategy for when to use `toast.error()` vs `ErrorDisplay` component for error presentation across the application. This strategy ensures consistent, user-friendly error handling while maintaining appropriate UX patterns for different error types.
## Core Principles
1. **Persistent errors** → Use `ErrorDisplay` (user needs to see and potentially act on the error)
2. **Transient errors** → Use `toast.error()` (quick feedback for temporary actions)
3. **Form validation** → Keep inline validation errors (field-level feedback)
4. **Critical errors** → Use `ErrorDisplay` with retry capability
5. **User action errors** → Use `ErrorDisplay` banner (dismissible, contextual)
## Decision Tree
```
Is the error related to form validation?
├─ YES → Use inline validation error (text-red-500, keep existing pattern)
└─ NO → Continue
Is the error from a query (data fetching)?
├─ YES → Use ErrorDisplay card/inline variant with retry
└─ NO → Continue
Is the error from a mutation (data update)?
├─ YES → Use ErrorDisplay banner variant (dismissible)
└─ NO → Continue
Is the error from a quick action (copy link, quick toggle)?
├─ YES → Use toast.error() (transient feedback)
└─ NO → Continue
Is the error critical (network failure, auth failure, runtime crash)?
├─ YES → Use ErrorDisplay with appropriate variant and retry
└─ NO → Use toast.error() as fallback
```
## ErrorDisplay Usage Guidelines
### When to Use ErrorDisplay
#### 1. Query Errors (Data Fetching)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use**: `ErrorDisplay` card or inline variant
**Variant**: `card` for page-level errors, `inline` for component-level errors
**Features**: Retry button, error details (dev mode)
**Examples**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Failed to load tracks/library
- Failed to load playlist details
- Failed to load user profile
- Failed to load marketplace items
```tsx
2026-01-13 18:47:57 +00:00
{
isError & & (
< ErrorDisplay
error={error}
variant="card"
severity="error"
context={{ action: 'fetching tracks', resource: 'tracks' }}
onRetry={() => refetch()}
/>
);
}
2026-01-11 16:01:27 +00:00
```
#### 2. Mutation Errors (Data Updates)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use**: `ErrorDisplay` banner variant
**Variant**: `banner` (dismissible, appears at top of page/content)
**Features**: Dismiss button, contextual error message
**Examples**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Failed to add track to playlist
- Failed to update profile
- Failed to delete resource
- Failed to share resource
```tsx
2026-01-13 18:47:57 +00:00
{
mutationError & & (
< ErrorDisplay
error={mutationError}
variant="banner"
severity="error"
onDismiss={() => setMutationError(null)}
/>
);
}
2026-01-11 16:01:27 +00:00
```
#### 3. Network Errors (API Failures)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use**: `ErrorDisplay` banner variant
**Variant**: `banner` or `card` depending on context
**Features**: Retry button, network-specific messaging
**Examples**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Connection timeout
- Server unavailable
- Network request failed
```tsx
< ErrorDisplay
error={networkError}
variant="banner"
severity="error"
context={{ action: 'connecting to server', resource: 'api' }}
onRetry={() => retryRequest()}
/>
```
#### 4. Runtime Errors (Component Crashes)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use**: `ErrorDisplay` in ErrorBoundary fallback
**Variant**: `card` or `modal`
**Features**: Error details, reload page action
**Examples**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Component render error
- Unhandled exception
- React error boundary catch
```tsx
< ErrorBoundary
fallback={
< ErrorDisplay
error={error}
variant="card"
severity="error"
actions={[
2026-01-13 18:47:57 +00:00
{ label: 'Reload Page', onClick: () => window.location.reload() },
2026-01-11 16:01:27 +00:00
]}
/>
}
>
{children}
< / ErrorBoundary >
```
#### 5. Auth Errors (Authentication Failures)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use**: `ErrorDisplay` inline variant
**Variant**: `inline`
**Features**: Contextual auth error message
**Examples**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Login failed
- Session expired
- Unauthorized access
```tsx
< ErrorDisplay
error={authError}
variant="inline"
severity="error"
context={{ action: 'authenticating', resource: 'auth' }}
/>
```
#### 6. Player Errors (Audio Playback)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use**: `ErrorDisplay` card variant
**Variant**: `card`
**Features**: Retry button, error type detection
**Examples**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Audio decode error
- Network error during playback
- Source file not found
```tsx
< ErrorDisplay
error={playerError}
variant="card"
severity="error"
context={{ action: 'playing audio', resource: 'player' }}
onRetry={() => retryPlayback()}
/>
```
### When NOT to Use ErrorDisplay
#### 1. Form Validation Errors
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use**: Inline validation errors (existing pattern)
**Reason**: Field-level feedback is more appropriate
**Pattern**: Keep existing `text-red-500` inline errors
```tsx
// ✅ Keep this pattern
2026-01-13 18:47:57 +00:00
{
errors.field & & (
< p className = "text-sm text-red-500" > {errors.field.message}< / p >
);
}
2026-01-11 16:01:27 +00:00
```
#### 2. Quick Action Errors (Transient)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use**: `toast.error()`
**Reason**: Quick feedback for temporary actions
**Examples**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Copy link to clipboard
- Quick toggle actions
- Temporary state changes
```tsx
// ✅ Keep toast for quick actions
try {
await navigator.clipboard.writeText(link);
toast.success('Link copied');
} catch {
toast.error('Failed to copy link');
}
```
#### 3. Validation Messages (Non-Critical)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use**: `toast.error()` or inline validation
**Reason**: User input validation feedback
**Examples**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- "Username is required"
- "Please type DELETE to confirm"
- "Cart is empty"
```tsx
// ✅ Keep toast for validation messages
if (!username) {
toast.error('Username is required');
return;
}
```
## Variant Selection Guide
### `inline` Variant
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use when**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Error is contextual to a specific component
- Error appears within content flow
- Error doesn't need to interrupt user flow
**Examples**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Form submission errors
- Component-level query errors
- Inline validation errors (if not using form validation pattern)
### `banner` Variant
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use when**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Error affects page-level functionality
- Error is dismissible
- Error should be prominent but not blocking
**Examples**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Mutation errors (add/update/delete)
- Network errors
- Session expiration warnings
### `card` Variant
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use when**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Error replaces main content
- Error needs visual prominence
- Error is part of content layout
**Examples**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Query errors (no data to show)
- Player errors (replaces player UI)
- Profile load errors
### `modal` Variant
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use when**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Error is critical and blocking
- User must acknowledge error
- Error requires immediate attention
**Examples**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Critical system errors
- Payment failures
- Security violations
## Severity Selection Guide
### `error` Severity (Default)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use when**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Operation failed
- Data cannot be loaded
- Action cannot be completed
**Visual**: Red colors, AlertCircle icon
### `warning` Severity
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use when**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Operation partially succeeded
- Warning that doesn't block functionality
- User should be aware but can continue
**Visual**: Yellow/gold colors, AlertTriangle icon
### `info` Severity
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
**Use when**:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- Informational message
- Non-critical notification
- Helpful context
**Visual**: Cyan colors, Info icon
## Migration Strategy
### Phase 1: Query Errors (HIGH Priority)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
1. Replace query error displays with ErrorDisplay card variant
2. Add retry functionality
3. Files: LibraryPage ✅, TrackDetailPage, MarketplaceHome, RolesPage, SettingsPage
### Phase 2: Mutation Errors (MEDIUM Priority)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
1. Replace mutation toast.errors with ErrorDisplay banner variant
2. Add dismiss functionality
3. Files: All feature files with mutations
### Phase 3: Component Errors (MEDIUM Priority)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
1. Replace PlayerError with ErrorDisplay card variant
2. Replace AuthErrorMessage with ErrorDisplay inline variant
3. Update ErrorBoundary fallback UI
### Phase 4: Network Errors (HIGH Priority)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
1. Replace API client toast.errors with ErrorDisplay banner
2. Add retry functionality
3. File: api/client.ts
### Phase 5: Keep Toast for Transient Actions (LOW Priority)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
1. Keep toast.error() for copy link, quick actions
2. Document exceptions
3. No changes needed
## Code Examples
### Query Error Pattern
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
```tsx
const { data, isError, error, refetch } = useQuery({
queryKey: ['tracks'],
queryFn: fetchTracks,
});
return (
< >
{isError ? (
< ErrorDisplay
error={error}
variant="card"
severity="error"
context={{ action: 'fetching tracks', resource: 'tracks' }}
onRetry={() => refetch()}
/>
) : (
< TrackList tracks = {data} / >
)}
< />
);
```
### Mutation Error Pattern
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
```tsx
const [mutationError, setMutationError] = useState< Error | null > (null);
const handleMutation = async () => {
try {
setMutationError(null);
await mutateAsync(data);
toast.success('Success');
} catch (error) {
const apiError = parseApiError(error);
setMutationError(new Error(apiError.message));
}
};
return (
< >
{mutationError & & (
< ErrorDisplay
error={mutationError}
variant="banner"
severity="error"
onDismiss={() => setMutationError(null)}
/>
)}
< Form onSubmit = {handleMutation} / >
< />
);
```
### Form Validation Pattern (Keep)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
```tsx
// ✅ Keep existing pattern for form validation
< FormField label = "Email" error = {errors.email?.message} >
< Input type = "email" { . . . register ( ' email ' ) } / >
< / FormField >
```
### Quick Action Pattern (Keep Toast)
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
```tsx
// ✅ Keep toast for quick actions
const handleCopyLink = async () => {
try {
await navigator.clipboard.writeText(link);
toast.success('Link copied');
} catch {
toast.error('Failed to copy link');
}
};
```
## Accessibility Considerations
### ErrorDisplay Component
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- ✅ ARIA `role="alert"` and `aria-live="polite"`
- ✅ Keyboard navigation for retry/dismiss buttons
- ✅ Screen reader announcements
- ✅ Focus management
### Toast Notifications
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- ✅ ARIA live regions (handled by toast library)
- ✅ Auto-dismiss with appropriate timing
- ✅ Keyboard accessible
### Form Validation
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
- ✅ Inline errors associated with form fields
- ✅ ARIA `aria-invalid` and `aria-describedby`
- ✅ Screen reader announcements
## Testing Strategy
1. **Query Errors** : Test retry functionality
2. **Mutation Errors** : Test dismiss functionality
3. **Network Errors** : Test retry and offline handling
4. **Form Validation** : Test inline error display
5. **Toast Errors** : Test transient error display
## Rollback Plan
If ErrorDisplay causes issues:
2026-01-13 18:47:57 +00:00
2026-01-11 16:01:27 +00:00
1. Revert to toast.error() for affected areas
2. Keep ErrorDisplay for query errors (most critical)
3. Gradually reintroduce ErrorDisplay for mutations
## References
- [ErrorDisplay Component API ](./ERROR_DISPLAY_COMPONENT_API.md )
- [Error Display Patterns Audit ](../../docs/ERROR_DISPLAY_PATTERNS_AUDIT.md )
- [Error Messages Utility ](../../utils/errorMessages.ts )
- [API Error Handler ](../../utils/apiErrorHandler.ts )