use-notifications (use-notifications)
A React hook for managing browser notifications with comprehensive feature detection and fallback support
v0.1.0
Usage
import { use-notifications } from "@/hooks"1"use client";
2
3import {
4  AlertCircleIcon,
5  BellIcon,
6  CheckCircle2Icon,
7  XCircleIcon,
8} from "lucide-react";
9
10import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
11import { Badge } from "@/components/ui/badge";
12import { Button } from "@/components/ui/button";
13import {
14  Card,
15  CardContent,
16  CardDescription,
17  CardFooter,
18  CardHeader,
19  CardTitle,
20} from "@/components/ui/card";
21
22import { useNotifications } from "@/components/hooks/use-notifications";
23
24export function UseNotificationsDemo() {
25  const {
26    permission,
27    requestPermission,
28    showNotification,
29    isSupported,
30    isSecureContext,
31    supportsActions,
32    supportsBadge,
33    supportsImage,
34    supportsVibrate,
35    supportsSound,
36  } = useNotifications();
37
38  const handleRequestPermission = async () => {
39    try {
40      await requestPermission();
41    } catch (error) {
42      console.error("Failed to request permission:", error);
43    }
44  };
45
46  const handleShowNotification = () => {
47    showNotification("Demo Notification", {
48      body: "This is a test notification from the demo component",
49      icon: "/favicon.ico",
50      badge: "/favicon.ico",
51      image: "/favicon.ico",
52      // Actions are only supported with Service Worker
53      // actions: [
54      //   {
55      //     action: 'view',
56      //     title: 'View Details',
57      //   },
58      // ],
59    });
60  };
61
62  return (
63    <Card className="relative max-w-2xl w-full">
64      <CardHeader>
65        <CardTitle className="flex items-center gap-2">
66          <BellIcon className="h-5 w-5" />
67          useNotifications
68        </CardTitle>
69        <CardDescription>
70          This component demonstrates the use of the{" "}
71          <code>useNotifications</code> hook to manage browser notifications.
72        </CardDescription>
73      </CardHeader>
74      <CardContent className="space-y-4">
75        <div className="space-y-2">
76          <h3 className="font-medium">Browser Support</h3>
77          <div className="flex flex-wrap gap-2">
78            <Badge variant={isSupported ? "default" : "destructive"}>
79              {isSupported ? "Supported" : "Not Supported"}
80            </Badge>
81            <Badge variant={isSecureContext ? "default" : "destructive"}>
82              {isSecureContext ? "Secure Context" : "Not Secure"}
83            </Badge>
84          </div>
85        </div>
86
87        <div className="space-y-2">
88          <h3 className="font-medium">Feature Support</h3>
89          <div className="flex flex-wrap gap-2">
90            <Badge variant={supportsActions ? "default" : "secondary"}>
91              Actions {supportsActions ? "✓" : "✗"}
92            </Badge>
93            <Badge variant={supportsBadge ? "default" : "secondary"}>
94              Badge {supportsBadge ? "✓" : "✗"}
95            </Badge>
96            <Badge variant={supportsImage ? "default" : "secondary"}>
97              Image {supportsImage ? "✓" : "✗"}
98            </Badge>
99            <Badge variant={supportsVibrate ? "default" : "secondary"}>
100              Vibrate {supportsVibrate ? "✓" : "✗"}
101            </Badge>
102            <Badge variant={supportsSound ? "default" : "secondary"}>
103              Sound {supportsSound ? "✓" : "✗"}
104            </Badge>
105          </div>
106        </div>
107
108        <div className="space-y-2">
109          <h3 className="font-medium">Permission Status</h3>
110          <Badge
111            variant={
112              permission === "granted"
113                ? "default"
114                : permission === "denied"
115                ? "destructive"
116                : "secondary"
117            }
118          >
119            {permission === "granted" ? (
120              <CheckCircle2Icon className="mr-1 h-3 w-3" />
121            ) : permission === "denied" ? (
122              <XCircleIcon className="mr-1 h-3 w-3" />
123            ) : (
124              <AlertCircleIcon className="mr-1 h-3 w-3" />
125            )}
126            {permission.charAt(0).toUpperCase() + permission.slice(1)}
127          </Badge>
128        </div>
129
130        {!isSupported && (
131          <Alert variant="destructive">
132            <AlertCircleIcon className="h-4 w-4" />
133            <AlertTitle>Not Supported</AlertTitle>
134            <AlertDescription>
135              Your browser does not support the Notifications API. Consider
136              using a fallback UI notification system.
137            </AlertDescription>
138          </Alert>
139        )}
140
141        {!isSecureContext && (
142          <Alert variant="destructive">
143            <AlertCircleIcon className="h-4 w-4" />
144            <AlertTitle>Not Secure</AlertTitle>
145            <AlertDescription>
146              The Notifications API requires a secure context (HTTPS). Please
147              use HTTPS to enable notifications.
148            </AlertDescription>
149          </Alert>
150        )}
151
152        {supportsActions && (
153          <Alert variant="default">
154            <AlertCircleIcon className="h-4 w-4" />
155            <AlertTitle>Actions Support</AlertTitle>
156            <AlertDescription>
157              Note: Notification actions are only supported when using a Service
158              Worker. This demo uses the basic Notification API without a
159              Service Worker.
160            </AlertDescription>
161          </Alert>
162        )}
163      </CardContent>
164      <CardFooter className="flex lg:flex-row items-start flex-col gap-2">
165        <Button
166          onClick={handleRequestPermission}
167          disabled={!isSupported || !isSecureContext}
168        >
169          Request Permission
170        </Button>
171        <Button
172          onClick={handleShowNotification}
173          disabled={
174            !isSupported || !isSecureContext || permission !== "granted"
175          }
176        >
177          Show Notification
178        </Button>
179      </CardFooter>
180    </Card>
181  );
182}
183Install
npx wkkkis-hooks add use-notificationsOptions
| Name | Type | Default | Description | 
|---|---|---|---|
| permission | 'default' | 'granted' | 'denied' | — | Current notification permission status | 
| isSupported | boolean | — | Whether the browser supports the Notifications API | 
| isSecureContext | boolean | — | Whether the current context is secure (HTTPS) | 
| supportsActions | boolean | — | Whether the browser supports notification actions | 
| supportsBadge | boolean | — | Whether the browser supports notification badges | 
| supportsImage | boolean | — | Whether the browser supports notification images | 
| supportsVibrate | boolean | — | Whether the browser supports notification vibration | 
| supportsSound | boolean | — | Whether the browser supports notification sounds | 
| requestPermission | () => Promise<NotificationPermission> | — | Function to request notification permission | 
| showNotification | (title: string, options?: NotificationOptions) => void | — | Function to show a notification | 
| body | string | — | The body text of the notification | 
| icon | string | — | The URL of an icon to display in the notification | 
| image | string | — | The URL of an image to display in the notification | 
| badge | string | — | The URL of an image used to represent the notification when there is not enough space | 
| tag | string | — | An ID for a given notification that allows you to find, replace, or remove the notification | 
| data | void | — | Arbitrary data that you want to associate with the notification | 
| requireInteraction | boolean | — | Whether the notification should remain active until the user clicks or dismisses it | 
| silent | boolean | — | Whether the notification should be silent | 
| sound | string | — | The URL of an audio file to play with the notification | 
| vibrate | number | number[] | — | A vibration pattern for devices with vibration hardware | 
| dir | 'auto' | 'ltr' | 'rtl' | — | The direction of the notification's text | 
| lang | string | — | The notification's language | 
| renotify | boolean | — | Whether the user should be notified after a new notification replaces an old one | 
| sticky | boolean | — | Whether the notification should be sticky | 
| timestamp | number | — | The time at which the notification was created | 
| actions | Array<{action: string, title: string, icon?: string}> | — | Actions to display in the notification | 
| The | — | — | hook returns an object with the following properties | 
| The | — | — | showNotification function accepts an optional options parameter | 
Browser Support
- The Notifications API is supported in most modern browsers with varying feature support:
 - Chrome: Full support
 - Firefox: Full support
 - Safari: Partial support (some features may not be available)
 - Edge: Full support
 - Opera: Full support
 - Note: The API requires a secure context (HTTPS) to work, except for localhost.
 
Feature Detection
- The hook automatically detects browser support for various notification features
 - Provides boolean flags for each supported feature
 - Handles cases where certain features might not be available
 - Gracefully degrades functionality based on browser capabilities
 
Permission Management
- Tracks the current permission status
 - Provides a function to request permission
 - Handles permission changes across browser tabs
 - Manages permission state persistence
 
Best Practices & Caveats
- Always check for browser support before using the hook
 - Request permission only after a user interaction
 - Provide clear feedback about the notification permission status
 - Implement fallback UI notifications for unsupported browsers
 - Use appropriate icons and badges to improve notification visibility
 - Consider the user's context when showing notifications
 - Handle notification errors gracefully
 - Clean up notifications when they're no longer needed
 
Files in this hook
| Source | Destination | 
|---|---|
| files/use-notifications.ts | src/hooks/use-notifications.ts |