React notification center widget for EduSight Notification Service, aligned with Novu's React UI and functionalities
npm install @edusight/notification-widgetbash
npm install @edusight/notification-widget
`
$3
The widget requires React 18+ as a peer dependency:
`bash
npm install react@^18.2.0 react-dom@^18.2.0
`
$3
Import the widget styles in your application:
`tsx
import '@edusight/notification-widget/styles';
// or
import '@edusight/notification-widget/styles.css';
`
$3
Each widget section exposes mx-widget-* hooks so host apps can override colors or spacing without touching the bundled CSS:
| Attribute | Target |
| --- | --- |
| data-mx-widget="root" | Widget shell/bell container |
| data-mx-widget="popover" | Notifications popover root |
| data-mx-widget="tabs" | Tab navigation bar |
| data-mx-widget="item" | Individual notification item |
| data-mx-widget="action" | Primary/secondary action buttons |
| data-mx-widget="bell" | Bell button itself |
Use these selectors in your global CSS to align the widget with your design system (for example .mx-widget-root { background-color: #fff; }). No need to import the widget CSS if you only rely on the shared CSS variables.
Quick Start
$3
The main component that provides a complete notification center experience:
`tsx
import { NotificationWidget } from '@edusight/notification-widget';
import '@edusight/notification-widget/styles';
function App() {
return (
sdkConfig={{
apiKey: 'your-api-key',
baseUrl: 'https://api.example.com',
subscriberId: 'user-123',
tenantId: 'your-tenant',
environmentId: 'production',
environment: 'production',
}}
position="right"
size="medium"
theme="light"
onError={(error) => console.error('Widget error:', error)}
/>
);
}
`
$3
For development, you can use the playground environment:
`bash
cd playground
npm install
npm run dev
`
The playground provides a complete testing environment with:
- Mock data for offline development
- Real-time WebSocket simulation
- All widget features and configurations
- Debug logging and status monitoring
Core Concepts
$3
The main widget component that provides a complete notification center with SDK dependency injection:
`tsx
import { NotificationWidget } from '@edusight/notification-widget';
sdkConfig={{
apiKey: 'your-api-key',
baseUrl: 'https://api.example.com',
subscriberId: 'user-123',
websocketUrl: 'wss://api.example.com',
environment: 'production',
}}
position="right"
size="medium"
theme="light"
className="custom-widget-class"
onError={(error) => console.error('Widget error:', error)}
/>
`
Props:
- sdkConfig: SDKConfiguration - SDK configuration for dependency injection
- position: 'left' | 'right' - Popover position relative to bell (default: 'right')
- size: 'small' | 'medium' | 'large' - Widget size (default: 'medium')
- theme: 'light' | 'dark' - Theme mode (default: 'light')
- className: string - Additional CSS classes
- onError: (error: Error) => void - Error handler callback
$3
The widget uses dependency injection to integrate with the EduSight Notification SDK:
`typescript
interface SDKConfiguration {
apiKey: string; // API key for authentication
baseUrl: string; // Base URL for the notification service
subscriberId: string; // Subscriber ID for the current user
tenantId: string; // Tenant ID for multi-tenant support
environmentId: string; // Environment ID for environment scoping
environment: 'development' | 'staging' | 'production'; // Environment setting
}
`
Note: WebSocket URL is automatically derived from baseUrl by the SDK. The SDK converts HTTP URLs to WebSocket URLs automatically (http → ws, https → wss).
$3
Access the SDK instance from within the widget context:
`tsx
import { useSDK } from '@edusight/notification-widget';
function CustomComponent() {
const { client, isInitialized, error } = useSDK();
if (!isInitialized) return Loading...;
if (error) return Error: {error.message};
// Use the SDK client for custom operations
const handleCustomAction = async () => {
await client.notifications.markAsRead('notification-id');
};
return ;
}
`
Components
$3
Main notification center component with complete functionality:
`tsx
import { NotificationWidget } from '@edusight/notification-widget';
sdkConfig={sdkConfig}
position="right"
size="medium"
theme="light"
onError={handleError}
/>
`
Features:
- Bell icon with unread count badge
- Popover with notification list
- Real-time WebSocket updates with fallback polling
- Live preferences management
- Error boundaries with fallback UI
- Cross-tab synchronization
- Optimistic updates
$3
Notification bell icon with unread badge (used internally by NotificationWidget):
`tsx
import { BellComponent } from '@edusight/notification-widget';
unreadCount={5}
onClick={handleClick}
size="medium"
disabled={false}
/>
`
$3
Notification popover content (used internally by NotificationWidget):
`tsx
import { InboxPopover } from '@edusight/notification-widget';
isOpen={true}
onClose={handleClose}
position="right"
currentView="notifications"
onViewChange={handleViewChange}
notifications={notifications}
onNotificationAction={handleAction}
preferences={preferences}
onPreferenceChange={handlePreferenceChange}
/>
`
$3
Individual notification item component:
`tsx
import { NotificationItem } from '@edusight/notification-widget';
notification={notification}
onAction={handleAction}
isSelected={false}
onSelect={handleSelect}
/>
`
$3
Notification preference management component:
`tsx
import { PreferencesView } from '@edusight/notification-widget';
preferences={preferences}
onChange={handleChange}
isLoading={false}
/>
`
$3
The widget includes comprehensive error boundaries:
`tsx
import {
NotificationWidgetErrorBoundary,
ComponentErrorBoundary
} from '@edusight/notification-widget';
// Main widget error boundary
// Component-level error boundary
`
Hooks
$3
Manage notification preferences with real-time saving:
`tsx
import { useLivePreferences } from '@edusight/notification-widget';
const {
updatePreference,
isSaving,
error
} = useLivePreferences({
preferences: currentPreferences,
onPreferencesChange: handleChange,
onError: handleError,
});
// Update a preference (automatically debounced and saved)
updatePreference('channels.email', true);
`
$3
Access the SDK instance within the widget context:
`tsx
import { useSDK } from '@edusight/notification-widget';
const { client, isInitialized, error } = useSDK();
if (!isInitialized) return Loading...;
// Use SDK methods directly
const markAsRead = async (id: string) => {
await client.notifications.markAsRead(id);
};
`
$3
The widget is designed to work with custom hooks that use the SDK:
`tsx
// Custom hook using the SDK
function useCustomNotifications() {
const { client } = useSDK();
const [notifications, setNotifications] = useState([]);
useEffect(() => {
if (client) {
client.notifications.list().then(setNotifications);
}
}, [client]);
return notifications;
}
`
Configuration
$3
`typescript
interface SDKConfiguration {
apiKey: string; // API key for authentication
baseUrl: string; // Base URL for the notification service
subscriberId: string; // Subscriber ID for the current user
tenantId: string; // Tenant ID for multi-tenant support
environmentId: string; // Environment ID for environment scoping
environment: 'development' | 'staging' | 'production'; // Environment setting
}
`
$3
`typescript
interface NotificationWidgetProps {
sdkConfig: SDKConfiguration; // SDK configuration for dependency injection
position?: 'left' | 'right'; // Popover position (default: 'right')
size?: 'small' | 'medium' | 'large'; // Widget size (default: 'medium')
theme?: 'light' | 'dark'; // Theme mode (default: 'light')
className?: string; // Additional CSS classes
onError?: (error: Error) => void; // Error handler callback
}
`
$3
The widget uses a reducer pattern for state management:
`typescript
interface WidgetState {
notifications: Notification[]; // Current notifications
unreadCount: number; // Unread notification count
preferences: NotificationPreferences; // User preferences
ui: {
isOpen: boolean; // Popover open state
currentView: 'notifications' | 'preferences'; // Current view
selectedNotifications: string[]; // Selected notification IDs
isLoading: boolean; // Loading state
error: Error | null; // Error state
};
websocket: {
connected: boolean; // WebSocket connection status
reconnecting: boolean; // Reconnection state
};
}
`
Styling & Theming
$3
The widget is built with Tailwind CSS and follows clean design principles. All components use Tailwind utility classes for consistent styling.
$3
The widget supports light and dark themes:
`tsx
// Light theme (default)
// Dark theme
`
$3
Override widget styles using CSS classes:
`tsx
className="custom-notification-widget"
sdkConfig={config}
/>
`
`css
.custom-notification-widget {
/ Custom styles for the widget container /
}
.custom-notification-widget .notification-bell {
/ Custom styles for the bell component /
}
.custom-notification-widget .notification-popover {
/ Custom styles for the popover /
}
`
$3
The widget uses CSS variables for theming:
`css
:root {
--notification-primary: #3b82f6;
--notification-primary-hover: #2563eb;
--notification-background: #ffffff;
--notification-text: #1f2937;
--notification-border: #e5e7eb;
}
[data-theme='dark'] {
--notification-primary: #60a5fa;
--notification-primary-hover: #3b82f6;
--notification-background: #1f2937;
--notification-text: #f9fafb;
--notification-border: #374151;
}
`
Performance Optimizations
$3
The refactored architecture provides several performance benefits:
- Dependency Injection: SDK is initialized once and reused across components
- Error Boundaries: Component-level error isolation prevents cascading failures
- Optimistic Updates: UI updates immediately while API calls happen in background
- Debounced Preferences: Preference changes are debounced to reduce API calls
- Fallback Polling: Automatic fallback to polling when WebSocket fails
$3
The widget includes optimized code splitting:
`tsx
// Main widget bundle: ~17KB gzipped
import { NotificationWidget } from '@edusight/notification-widget';
// Components are automatically code-split
// - Bell component: Loaded immediately
// - Popover content: Loaded on first open
// - Preferences view: Loaded when accessed
`
$3
The widget includes proper cleanup:
- WebSocket connections are properly closed
- Event listeners are removed on unmount
- Timers and intervals are cleared
- Component state is reset on errors
$3
Optimized bundle sizes:
- Main widget: ~17KB gzipped
- SDK integration: ~5KB additional
- Styles: ~25KB (includes Tailwind utilities)
- Total: ~47KB gzipped (complete package)
Accessibility
The widget includes:
- ✅ WCAG 2.1 Level AA compliance
- ✅ Keyboard navigation (Tab, Enter, Escape)
- ✅ ARIA labels and roles
- ✅ Screen reader support
- ✅ High contrast mode support
- ✅ Focus management
Use the AccessibilityProvider for custom accessibility options:
`tsx
import { AccessibilityProvider } from '@edusight/notification-widget';
`
Playground Development Environment
A comprehensive development environment is included for testing and debugging:
`bash
cd playground
npm install
npm run dev
`
The playground demonstrates:
- Complete NotificationWidget integration
- SDK dependency injection pattern
- Real-time WebSocket simulation
- All notification operations (CRUD)
- Live preferences management
- Error boundary testing
- Cross-tab synchronization
- Responsive design testing
- Debug logging and monitoring
$3
- Configuration Panel: Test different SDK configurations
- Debug Console: Real-time logging and status monitoring
- Connection Status: API and WebSocket connection indicators
- Feature Testing: All widget features in one place
- Error Simulation: Test error boundaries and fallbacks
See the playground README for detailed development documentation.
Build Status
| Component | Status | Bundle Size | Features |
|-----------|--------|-------------|----------|
| Widget | ✅ Success | ~17 KB (gzipped) | Complete notification center |
| SDK Integration | ✅ Success | ~5 KB (gzipped) | Dependency injection |
| Styles | ✅ Success | ~25 KB (gzipped) | Tailwind utilities |
| Playground | ✅ Success | ~330 KB (gzipped) | Development environment |
Development
$3
`bash
npm run build
`
Generates:
- dist/index.esm.js - ES modules build
- dist/index.cjs.js - CommonJS build
- dist/index.d.ts - TypeScript declarations
- dist/index.css - Compiled styles
$3
`bash
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage report
`
Test coverage: >90% across all components and hooks
$3
`bash
npm run lint # Check for issues
npm run lint:fix # Fix auto-fixable issues
`
$3
`bash
npm run type-check # TypeScript type checking
`
Integration Workflow
1. Install the widget: npm install @edusight/notification-widget
2. Import styles: import '@edusight/notification-widget/styles'
3. Configure SDK: Set up your SDK configuration object
4. Add NotificationWidget: Place the widget component in your app
5. Handle errors: Implement error handling for production use
6. Test with playground: Use the included playground for development
$3
`tsx
import React from 'react';
import { NotificationWidget } from '@edusight/notification-widget';
import '@edusight/notification-widget/styles';
function App() {
const sdkConfig = {
apiKey: process.env.REACT_APP_NOTIFICATION_API_KEY!,
baseUrl: process.env.REACT_APP_NOTIFICATION_API_URL!,
subscriberId: getCurrentUserId(),
tenantId: getCurrentTenantId(),
environmentId: getCurrentEnvironmentId(),
environment: process.env.NODE_ENV as 'development' | 'staging' | 'production',
};
const handleError = (error: Error) => {
console.error('Notification widget error:', error);
// Send to error tracking service
errorTracker.captureException(error);
};
return (
My Application
sdkConfig={sdkConfig}
position="right"
size="medium"
theme="light"
onError={handleError}
/>
{/ Your app content /}
);
}
export default App;
`
Troubleshooting
$3
Ensure your component is used within the NotificationWidget context:
`tsx
// ❌ Wrong - useSDK outside widget context
function MyComponent() {
const { client } = useSDK(); // This will throw an error
return ...;
}
// ✅ Correct - Custom component within widget
{/ Custom components can use useSDK here /}
`
$3
1. Check required fields: Ensure all required SDK config fields are provided
2. Verify URLs: Check that baseUrl and websocketUrl are correct
3. Environment mismatch: Ensure environment matches your deployment
4. API key validation: Verify the API key is valid and has proper permissions
$3
1. URL format: Ensure WebSocket URL uses ws:// or wss:// protocol
2. CORS settings: Check CORS configuration on the backend
3. Firewall/proxy: Verify WebSocket connections aren't blocked
4. Fallback polling: The widget automatically falls back to polling if WebSocket fails
$3
1. Bundle size: Check if you're importing the entire widget when you only need parts
2. Re-renders: Use React DevTools to identify unnecessary re-renders
3. Memory leaks: Ensure proper cleanup of event listeners and timers
4. Error boundaries: Check if error boundaries are catching and handling errors properly
$3
1. CSS conflicts: Ensure your CSS doesn't conflict with widget styles
2. Tailwind purging: Make sure Tailwind isn't purging widget classes
3. Theme application: Verify theme prop is being applied correctly
4. Custom classes: Check that custom className prop is being used
API Reference
$3
`typescript
export {
NotificationWidget, // Main widget component
BellComponent, // Bell icon with badge
InboxPopover, // Popover content
NotificationItem, // Individual notification
PreferencesView, // Preferences management
NotificationWidgetErrorBoundary, // Main error boundary
ComponentErrorBoundary, // Component-level error boundary
};
`
$3
`typescript
export {
useSDK, // Access SDK instance
useLivePreferences, // Live preferences management
};
`
$3
`typescript
export type {
NotificationWidgetProps, // Main widget props
SDKConfiguration, // SDK config interface
WidgetState, // Widget state interface
WidgetAction, // State action types
Notification, // Core notification type
NotificationPreferences, // Preferences interface
WidgetError, // Error interface
};
`
$3
`typescript
export type {
Notification as SDKNotification,
NotificationFilters,
} from '@edusight/notification-sdk';
`
Architecture
$3
The widget follows clean architecture principles:
1. Separation of Concerns: UI, business logic, and data access are separated
2. Dependency Injection: SDK is injected rather than directly imported
3. Error Boundaries: Component-level error isolation
4. State Management: Centralized state with reducer pattern
5. Event Handling: Proper event cleanup and memory management
$3
`
NotificationWidget
├── SDKProvider (Dependency Injection)
├── NotificationWidgetErrorBoundary
└── NotificationWidgetInternal
├── ComponentErrorBoundary (BellComponent)
│ └── BellComponent
└── ComponentErrorBoundary (InboxPopover)
└── InboxPopover
├── NotificationsList
│ └── NotificationItem[]
└── PreferencesView
``