A React Native Nitro Module providing native iOS AlarmKit bindings for scheduling system-level alarms that work even when your app is closed.
- π΅ Apple AlarmKit API (iOS 26+)
- β° Fixed, Repeating & Timer Alarms
- π§ Progressive Bells for meditation apps
- π¨ Customizable Alarm UI
- π Custom Alarm Sounds
- π Stop Individual or All Alarms
Important
- Works in both Expo & Bare (Non Expo) React Native projects.
- iOS 26+ only β Android returns no-op (
null/false/[]) for all methods. - Requires physical device β Simulator has limited support.
- AlarmKit was introduced in iOS 26 (WWDC 2025).
npm install react-native-nitro-ios-alarm-kit react-native-nitro-modulesThen run:
cd ios && pod install| π iOS Demo | π Permission Prompt |
|---|---|
|
|
Add the AlarmKit usage description to your Info.plist:
<key>NSAlarmKitUsageDescription</key>
<string>Your app wants to schedule alerts for alarms you create.</string>To use custom alarm sounds:
- Add your sound file (e.g.,
magic.wav) to your Xcode project's main bundle - Supported formats:
.wav,.aiff,.caf - Pass the filename without extension to the
soundNameparameter
No configuration needed β all methods return false/null/[] as AlarmKit is iOS-only.
| Feature | Description |
|---|---|
| Fixed Alarms | Schedule one-time alarms at a specific timestamp |
| Relative Alarms | Schedule repeating alarms (daily, weekly) at a specific time |
| Timers | Schedule countdown timers that fire after a duration |
| Progressive Bells | Schedule meditation bells with pattern: t+1, t+2, t+3, t-1, t-2, t-3 |
| Stop Alarm | Cancel a specific alarm by ID |
| Stop All Alarms | Cancel all scheduled/firing alarms |
| Custom UI | Customize button text, colors, and SF Symbols icons |
| Custom Sounds | Use your own alarm sounds from the app bundle |
The Dynamic Island has very limited space. Follow these tips for best results:
| Tip | Why |
|---|---|
| Keep titles under 15 characters | Longer titles get truncated |
| Use distinctive SF Symbol icons | Only the icon shows in Dynamic Island |
| Set short app display name | Your CFBundleDisplayName shows in alarm UI |
Good icons for Dynamic Island:
- Stop:
checkmark.circle.fill,stop.circle.fill,xmark.circle.fill - Snooze:
repeat.circle.fill,clock.arrow.circlepath,zzz - Meditation:
leaf.fill,sparkles,wind
import { isAvailable } from 'react-native-nitro-ios-alarm-kit';
if (isAvailable()) {
// AlarmKit is available (iOS 26+)
} else {
// Fallback to local notifications
}import { requestAlarmPermission } from 'react-native-nitro-ios-alarm-kit';
const authorized = await requestAlarmPermission();
if (authorized) {
// User granted alarm permission
} else {
// Permission denied
}import { scheduleTimer } from 'react-native-nitro-ios-alarm-kit';
// Schedule a 5-minute timer
const alarmId = await scheduleTimer(
'Done! π', // title (keep SHORT!)
{
text: 'Stop',
textColor: '#FFFFFF',
icon: 'checkmark.circle.fill',
},
'#FF6B6B', // tintColor
300, // 5 minutes in seconds
{
text: 'Snooze',
textColor: '#FFFFFF',
icon: 'repeat.circle.fill',
},
'magic' // Custom sound: magic.wav (optional)
);
if (alarmId) {
console.log('Timer scheduled with ID:', alarmId);
}import { scheduleFixedAlarm } from 'react-native-nitro-ios-alarm-kit';
// Schedule alarm for 1 hour from now
const timestamp = Date.now() / 1000 + 3600;
const alarmId = await scheduleFixedAlarm(
'Wake Up!', // title (keep SHORT!)
{
text: 'Stop',
textColor: '#FFFFFF',
icon: 'checkmark.circle.fill',
},
'#FF5733', // tintColor
{
text: 'Snooze',
textColor: '#FFFFFF',
icon: 'repeat.circle.fill',
},
timestamp, // Unix timestamp in seconds
{ postAlert: 540 }, // 9-min snooze (like iOS default)
'alarm_sound' // Custom sound (optional)
);
if (alarmId) {
console.log('Alarm scheduled with ID:', alarmId);
}import { scheduleRelativeAlarm } from 'react-native-nitro-ios-alarm-kit';
// Schedule alarm for 7:00 AM on weekdays
const alarmId = await scheduleRelativeAlarm(
'Wake Up!', // title (keep SHORT!)
{
text: 'Stop',
textColor: '#FFFFFF',
icon: 'sun.max.fill',
},
'#FF9500', // tintColor
7, // hour (0-23)
0, // minute (0-59)
['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
{
text: 'Snooze',
textColor: '#FFFFFF',
icon: 'moon.zzz.fill',
},
{ postAlert: 540 }, // 9-min snooze
'morning_alarm' // Custom sound (optional)
);
if (alarmId) {
console.log('Daily alarm scheduled with ID:', alarmId);
}Perfect for meditation apps! Schedules bells in pattern: t+1, t+2, t+3, t-1, t-2, t-3 where t is the base time and the number is multiplied by the interval.
import { scheduleProgressiveBells } from 'react-native-nitro-ios-alarm-kit';
// Schedule bells around a meditation session ending in 10 minutes
// With 30-second intervals: bells at t+30s, t+60s, t+90s, t-30s, t-60s, t-90s
const baseTimestamp = Date.now() / 1000 + 600; // 10 mins from now
const bellIds = await scheduleProgressiveBells(
'Breathe', // title (keep SHORT!)
{
text: 'OK',
textColor: '#FFFFFF',
icon: 'leaf.fill',
},
'#4ECDC4', // tintColor
baseTimestamp, // Base time (the "t" reference point)
30, // 30-second intervals
undefined, // No secondary button
'singing_bowl' // Custom sound (optional)
);
console.log(`Scheduled ${bellIds.length} bells:`, bellIds);import { stopAlarm } from 'react-native-nitro-ios-alarm-kit';
// Stop/cancel a specific alarm by ID
const success = await stopAlarm(alarmId);
if (success) {
console.log('Alarm stopped');
}import { stopAllAlarms } from 'react-native-nitro-ios-alarm-kit';
// Stop/cancel all scheduled and firing alarms
const success = await stopAllAlarms();
if (success) {
console.log('All alarms stopped');
}Returns true if AlarmKit is available (iOS 26+), false otherwise.
Requests permission to schedule alarms.
Returns: true if authorized, false otherwise.
Stops or cancels a specific alarm by ID.
| Parameter | Type | Required | Description |
|---|---|---|---|
alarmId |
string |
β | UUID string of alarm to stop |
Returns: true if successful, false otherwise.
Stops all scheduled and currently firing alarms.
Returns: true if successful, false otherwise.
Schedules a countdown timer that fires after the specified duration.
| Parameter | Type | Required | Description |
|---|---|---|---|
title |
string |
β | Timer title (keep under 15 chars) |
stopBtn |
CustomizableAlarmButton |
β | Primary stop button configuration |
tintColor |
string |
β | Hex color (e.g., #FF6B6B) |
durationSeconds |
number |
β | Timer duration in seconds |
secondaryBtn |
CustomizableAlarmButton |
β | Optional secondary button |
soundName |
string |
β | Sound file name without extension |
Returns: Alarm ID (UUID string) or null if failed.
Schedules a one-time alarm at a specific Unix timestamp.
| Parameter | Type | Required | Description |
|---|---|---|---|
title |
string |
β | Alarm title (keep under 15 chars) |
stopBtn |
CustomizableAlarmButton |
β | Primary stop button configuration |
tintColor |
string |
β | Hex color (e.g., #FF5733) |
secondaryBtn |
CustomizableAlarmButton |
β | Optional secondary button (Snooze) |
timestamp |
number |
β | Unix timestamp in seconds |
countdown |
AlarmCountdown |
β | Snooze duration configuration |
soundName |
string |
β | Sound file name without extension |
Returns: Alarm ID (UUID string) or null if failed.
Schedules a repeating alarm at a specific time on given weekdays.
| Parameter | Type | Required | Description |
|---|---|---|---|
title |
string |
β | Alarm title (keep under 15 chars) |
stopBtn |
CustomizableAlarmButton |
β | Primary stop button configuration |
tintColor |
string |
β | Hex color |
hour |
number |
β | Hour (0-23) |
minute |
number |
β | Minute (0-59) |
repeats |
AlarmWeekday[] |
β | Days to repeat |
secondaryBtn |
CustomizableAlarmButton |
β | Optional secondary button |
countdown |
AlarmCountdown |
β | Snooze duration configuration |
soundName |
string |
β | Sound file name without extension |
Returns: Alarm ID (UUID string) or null if failed.
Schedules meditation bells with progressive pattern: t+1, t+2, t+3, t-1, t-2, t-3.
| Parameter | Type | Required | Description |
|---|---|---|---|
title |
string |
β | Bell title (keep under 15 chars) |
stopBtn |
CustomizableAlarmButton |
β | Stop button configuration |
tintColor |
string |
β | Hex color (e.g., #4ECDC4) |
baseTimestamp |
number |
β | Base Unix timestamp (the "t" reference) |
intervalSeconds |
number |
β | Interval between bells in seconds |
secondaryBtn |
CustomizableAlarmButton |
β | Optional secondary button |
soundName |
string |
β | Sound file name without extension |
Returns: Array of alarm IDs (UUID strings) for scheduled bells.
Example timeline with intervalSeconds = 30 and baseTimestamp = t:
| Bell | Offset | Time |
|---|---|---|
| 1 | +1 | t + 30s |
| 2 | +2 | t + 60s |
| 3 | +3 | t + 90s |
| 4 | -1 | t - 30s |
| 5 | -2 | t - 60s |
| 6 | -3 | t - 90s |
interface CustomizableAlarmButton {
text: string; // Button label
textColor: string; // Hex color (e.g., '#FFFFFF')
icon?: string; // SF Symbol name (e.g., 'checkmark.circle.fill')
}
interface AlarmCountdown {
preAlert?: number; // Seconds before alarm (for countdown display)
postAlert?: number; // Snooze duration in seconds
}
type AlarmWeekday =
| 'monday'
| 'tuesday'
| 'wednesday'
| 'thursday'
| 'friday'
| 'saturday'
| 'sunday';import { useState } from 'react';
import { View, Text, Pressable, Alert } from 'react-native';
import {
isAvailable,
requestAlarmPermission,
scheduleTimer,
scheduleFixedAlarm,
scheduleProgressiveBells,
stopAlarm,
stopAllAlarms,
} from 'react-native-nitro-ios-alarm-kit';
export default function App() {
const [authorized, setAuthorized] = useState(false);
const [alarmIds, setAlarmIds] = useState<string[]>([]);
// 1. Check availability & request permission
const handleSetup = async () => {
if (!isAvailable()) {
Alert.alert('Error', 'AlarmKit requires iOS 26+');
return;
}
const result = await requestAlarmPermission();
setAuthorized(result);
};
// 2. Schedule a timer
const handleTimer = async () => {
const id = await scheduleTimer(
'Done!',
{ text: 'Stop', textColor: '#FFF', icon: 'checkmark.circle.fill' },
'#FF6B6B',
10 // 10 seconds
);
if (id) setAlarmIds((prev) => [...prev, id]);
};
// 3. Schedule meditation bells
const handleBells = async () => {
const ids = await scheduleProgressiveBells(
'Breathe',
{ text: 'OK', textColor: '#FFF', icon: 'leaf.fill' },
'#4ECDC4',
Date.now() / 1000 + 60, // Base: 1 min from now
10 // 10-second intervals
);
setAlarmIds((prev) => [...prev, ...ids]);
};
// 4. Stop last alarm
const handleStopLast = async () => {
if (alarmIds.length === 0) return;
const lastId = alarmIds[alarmIds.length - 1];
const success = await stopAlarm(lastId);
if (success) setAlarmIds((prev) => prev.slice(0, -1));
};
// 5. Stop all alarms
const handleStopAll = async () => {
const success = await stopAllAlarms();
if (success) setAlarmIds([]);
};
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 20, gap: 12 }}>
<Text>Scheduled: {alarmIds.length} alarm(s)</Text>
<Pressable onPress={handleSetup}>
<Text>π Request Permission</Text>
</Pressable>
<Pressable onPress={handleTimer} disabled={!authorized}>
<Text>β±οΈ Schedule Timer</Text>
</Pressable>
<Pressable onPress={handleBells} disabled={!authorized}>
<Text>π§ Schedule Bells</Text>
</Pressable>
<Pressable onPress={handleStopLast}>
<Text>βΉοΈ Stop Last</Text>
</Pressable>
<Pressable onPress={handleStopAll}>
<Text>π Stop All</Text>
</Pressable>
</View>
);
}| Platform | Status |
|---|---|
| iOS 26+ | β Fully Supported |
| iOS < 26 | false/null/[] |
| iOS Simulator | |
| Android | β No-op (returns false/null/[]) |
Pull requests welcome!
MIT Β© Gautham Vijayan
Made with β€οΈ and Nitro Modules

