@silicon.js/push-notifications
v1.0.9
Published
A React Native package for managing push notifications using Expo Notifications with a simple provider-based architecture.
Maintainers
Readme
Push Notifications Package
A React Native package for managing push notifications using Expo Notifications with a simple provider-based architecture.
Features
- 🔔 Easy-to-use React Context API for push notifications
- 🎯 Permission management with status tracking
- 🔄 Automatic token refresh and synchronization
- 💾 Token caching to avoid redundant API calls
- 📱 Cross-platform support (iOS & Android)
- 🎨 Customizable notification channels (Android)
- ⚡ App state change detection for token updates
Installation
npm install expo-notifications expo-application @react-native-async-storage/async-storageSetup
1. Wrap your app with the provider
import { PushNotificationsProvider } from './push-notifications';
function App() {
return (
<PushNotificationsProvider
projectID="your-expo-project-id"
onSendToken={async (params) => {
// Send token to your backend
const response = await api.post('/push-tokens', params);
return response.data;
}}
onDisableToken={async (params) => {
// Disable token on your backend
await api.delete(`/push-tokens/${params.deviceId}`);
}}
getTokenFromResponse={(response) => response.token}
>
{/* Your app components */}
</PushNotificationsProvider>
);
}2. Use the hook in your components
import { usePushNotificationsContext } from './push-notifications';
function NotificationSettings() {
const { token, permission, isPermissionGranted, requestPermission, sendToken, updateTokenIfNeeded } =
usePushNotificationsContext();
const handleEnableNotifications = async () => {
const status = await requestPermission();
if (status === 'granted') {
await updateTokenIfNeeded();
}
};
return (
<View>
<Text>Permission: {permission?.status}</Text>
<Text>Token: {token}</Text>
<Button onPress={handleEnableNotifications} title="Enable Notifications" />
</View>
);
}API Reference
PushNotificationsProvider Props
| Prop | Type | Required | Description |
| ---------------------- | -------------------------------------------------- | -------- | ----------------------------------------------------------- |
| projectID | string | Yes | Your Expo project ID |
| onSendToken | (params: CreatePushTokenDto) => Promise<unknown> | Yes | Callback to send token to your backend |
| onDisableToken | (params: DisablePushTokenDto) => Promise<void> | Yes | Callback to disable token on your backend |
| getTokenFromResponse | (response: Record<string, unknown>) => string | No | Extract token from API response (default: response.token) |
| children | React.ReactNode | Yes | Child components |
| fallback | React.ReactNode | No | Fallback UI while loading |
Context Values
State
token: string | null- The current push notification tokenpermission: PermissionDetails | null- Current permission status and detailsisPermissionGranted: boolean- Quick check if permission is granted
Methods
requestPermission(options?: GetPermissionOptions): Promise<PushNotificationsPermissionStatusEnum>- Request push notification permissions from the user
- Returns the permission status
sendToken(params: CreatePushTokenDto): Promise<void>- Manually send token to your backend
- Automatically caches the sent token
disableToken(params: DisablePushTokenDto): Promise<void>- Disable token on your backend
- Clears the cached token
updateTokenIfNeeded(): Promise<void>- Checks if the current token differs from the last sent token
- Automatically sends the token if it has changed
clearPushTokenCache(): Promise<void>- Clears the locally cached token
- Useful for testing or manual cache management
Types
CreatePushTokenDto
interface CreatePushTokenDto {
token: string;
deviceId: string;
}DisablePushTokenDto
interface DisablePushTokenDto {
deviceId: string;
}PermissionDetails
interface PermissionDetails {
status: PushNotificationsPermissionStatusEnum;
canAskAgain: boolean;
}PushNotificationsPermissionStatusEnum
enum PushNotificationsPermissionStatusEnum {
Granted = 'granted',
Denied = 'denied',
Undetermined = 'undetermined',
}GetPermissionOptions
interface GetPermissionOptions {
channelConfig?: Partial<Notifications.NotificationChannelInput>;
permissionsRequest?: Notifications.NotificationPermissionsRequest;
}Advanced Usage
Custom Notification Channel (Android)
const status = await requestPermission({
channelConfig: {
name: 'Important Updates',
importance: Notifications.AndroidImportance.HIGH,
sound: 'notification.wav',
vibrationPattern: [0, 250, 250, 250],
},
});Handling Token Updates
The package automatically handles token updates when:
- App returns to foreground
- Permission is granted
- Token is refreshed
You can also manually trigger updates:
useEffect(() => {
updateTokenIfNeeded();
}, [updateTokenIfNeeded]);Storage
The package uses AsyncStorage to cache the last sent token under the key STORAGE_KEYS.LAST_SENT_TOKEN. This prevents unnecessary API calls when the token hasn't changed.
Platform-Specific Notes
Android
- Automatically creates a default notification channel
- Uses
Application.getAndroidId()for device identification
iOS
- Uses
Application.getIosIdForVendorAsync()for device identification - Requires proper entitlements for push notifications
Error Handling
Always wrap push notification operations in try-catch blocks:
try {
await updateTokenIfNeeded();
} catch (error) {
console.error('Failed to update push token:', error);
// Handle error appropriately
}Best Practices
- Request permissions contextually - Don't ask for permissions immediately on app launch
- Handle denials gracefully - Provide UI to guide users to settings if needed
- Test on real devices - Push notifications don't work on simulators/emulators
- Monitor token changes - The package handles this automatically, but be aware tokens can change
- Clean up tokens - Call
disableToken()on logout or when notifications should be disabled
