Skip to main content

Notification Hooks

React hooks for sending notifications through email, SMS, and push notification channels.

Session required (publishable key)

When using a publishable key, include session in every notification call. Example: import { session } from './config'.

useNotification

Send notifications with automatic loading states and error handling.

Send Email Notification

import { useNotification } from '@ductape/react';
import { session } from './config';

function WelcomeEmail({ userEmail, userName }: { userEmail: string; userName: string }) {
const { mutate: sendNotification, isLoading, error } = useNotification({
onSuccess: () => {
alert('Welcome email sent!');
}
});

const handleSend = () => {
sendNotification({
channel: 'email',
to: userEmail,
template: 'welcome-email',
data: {
name: userName,
loginLink: 'https://app.com/login'
},
session,
});
};

return (
<button onClick={handleSend} disabled={isLoading}>
{isLoading ? 'Sending...' : 'Send Welcome Email'}
</button>
);
}

Send SMS Notification

import { session } from './config';

function SendVerificationCode({ phoneNumber }: { phoneNumber: string }) {
const [code] = useState(() => Math.floor(100000 + Math.random() * 900000).toString());

const { mutate, isLoading } = useNotification({
onSuccess: () => {
alert('Verification code sent!');
}
});

const handleSend = () => {
mutate({
channel: 'sms',
to: phoneNumber,
template: 'verification-code',
data: {
code,
expiresIn: '10 minutes'
},
session,
});
};

return (
<button onClick={handleSend} disabled={isLoading}>
{isLoading ? 'Sending...' : 'Send Code'}
</button>
);
}

Send Push Notification

import { session } from './config';

function SendPushNotification({ deviceToken, message }: { deviceToken: string; message: string }) {
const { mutate, isLoading } = useNotification();

const handleSend = () => {
mutate({
channel: 'push',
to: deviceToken,
data: {
title: 'New Message',
body: message,
badge: 1,
sound: 'default'
},
session,
});
};

return (
<button onClick={handleSend} disabled={isLoading}>
Send Push
</button>
);
}

useNotificationBatch

Send notifications to multiple recipients.

function BulkEmailSender() {
const [recipients, setRecipients] = useState<string[]>([]);

const { mutate: sendBatch, isLoading } = useNotificationBatch({
onSuccess: (result) => {
alert(`Sent ${result.successCount} emails successfully`);
}
});

const handleSendAll = () => {
sendBatch({
channel: 'email',
template: 'newsletter',
recipients: recipients.map(email => ({
to: email,
data: {
unsubscribeLink: `https://app.com/unsubscribe?email=${email}`
}
}))
});
};

return (
<div>
<textarea
placeholder="Enter emails (one per line)"
onChange={(e) => setRecipients(e.target.value.split('\n').filter(Boolean))}
/>
<button onClick={handleSendAll} disabled={isLoading || recipients.length === 0}>
{isLoading ? `Sending to ${recipients.length} recipients...` : 'Send Newsletter'}
</button>
</div>
);
}

useNotificationStatus

Track notification delivery status.

function NotificationTracker({ notificationId }: { notificationId: string }) {
const { data: status, isLoading } = useNotificationStatus(notificationId, {
refetchInterval: 5000 // Poll every 5 seconds
});

if (isLoading) return <div>Loading status...</div>;

return (
<div className="notification-status">
<h3>Notification Status</h3>
<div>
<span>Sent:</span> {status?.sent ? '✓' : '✗'}
</div>
<div>
<span>Delivered:</span> {status?.delivered ? '✓' : '✗'}
</div>
<div>
<span>Opened:</span> {status?.opened ? '✓' : '✗'}
</div>
<div>
<span>Clicked:</span> {status?.clicked ? '✓' : '✗'}
</div>
{status?.deliveredAt && (
<div>
<span>Delivered at:</span> {new Date(status.deliveredAt).toLocaleString()}
</div>
)}
</div>
);
}

Complete Examples

Order Confirmation

function OrderConfirmation({ order }: { order: any }) {
const { mutate: sendEmail } = useNotification();
const { mutate: sendSMS } = useNotification();
const { mutate: sendPush } = useNotification();

useEffect(() => {
sendOrderNotifications();
}, [order.id]);

const sendOrderNotifications = async () => {
// Send email
await sendEmail({
channel: 'email',
to: order.customerEmail,
template: 'order-confirmation',
data: {
orderNumber: order.id,
items: order.items,
total: order.total,
trackingLink: order.trackingUrl
}
});

// Send SMS
if (order.customerPhone) {
await sendSMS({
channel: 'sms',
to: order.customerPhone,
template: 'order-sms',
data: {
orderNumber: order.id,
trackingLink: order.trackingUrl
}
});
}

// Send push notification
if (order.deviceToken) {
await sendPush({
channel: 'push',
to: order.deviceToken,
data: {
title: 'Order Confirmed',
body: `Your order #${order.id} has been confirmed`,
badge: 1
}
});
}
};

return (
<div className="order-confirmation">
<h2>Order Confirmed!</h2>
<p>Order #{order.id}</p>
<p>Confirmation sent to {order.customerEmail}</p>
</div>
);
}

Password Reset

function ForgotPasswordForm() {
const [email, setEmail] = useState('');

const { mutate: sendReset, isLoading, error } = useNotification({
onSuccess: () => {
alert('Password reset link sent! Check your email.');
}
});

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();

// Generate reset token (in real app, do this on server)
const resetToken = Math.random().toString(36).substring(2);

sendReset({
channel: 'email',
to: email,
template: 'password-reset',
data: {
resetLink: `https://app.com/reset-password?token=${resetToken}`,
expiresIn: '1 hour'
}
});
};

return (
<form onSubmit={handleSubmit}>
<h2>Forgot Password</h2>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter your email"
required
/>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Sending...' : 'Send Reset Link'}
</button>
{error && <p className="error">{error.message}</p>}
</form>
);
}

Team Invitation

function InviteTeamMember() {
const [email, setEmail] = useState('');
const [role, setRole] = useState('member');
const { session } = useSession();

const { mutate: sendInvite, isLoading } = useNotification({
onSuccess: () => {
alert('Invitation sent!');
setEmail('');
}
});

const handleInvite = (e: React.FormEvent) => {
e.preventDefault();

sendInvite({
channel: 'email',
to: email,
template: 'team-invitation',
data: {
inviterName: session.user.name,
teamName: session.team.name,
role,
acceptLink: `https://app.com/invitations/accept?email=${email}&role=${role}`
}
});
};

return (
<form onSubmit={handleInvite}>
<h3>Invite Team Member</h3>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email address"
required
/>
<select value={role} onChange={(e) => setRole(e.target.value)}>
<option value="member">Member</option>
<option value="admin">Admin</option>
<option value="owner">Owner</option>
</select>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Sending...' : 'Send Invitation'}
</button>
</form>
);
}

Scheduled Notification

function ScheduleReminder() {
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
const [scheduledTime, setScheduledTime] = useState('');

const { mutate, isLoading } = useNotification({
onSuccess: () => {
alert('Reminder scheduled!');
}
});

const handleSchedule = (e: React.FormEvent) => {
e.preventDefault();

mutate({
channel: 'email',
to: email,
template: 'reminder',
data: {
message,
scheduledFor: scheduledTime
},
scheduledFor: new Date(scheduledTime)
});
};

return (
<form onSubmit={handleSchedule}>
<h3>Schedule Reminder</h3>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Reminder message"
required
/>
<input
type="datetime-local"
value={scheduledTime}
onChange={(e) => setScheduledTime(e.target.value)}
required
/>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Scheduling...' : 'Schedule Reminder'}
</button>
</form>
);
}

Multi-Channel Notification

function MultiChannelAlert({ userId, message }: { userId: string; message: string }) {
const { data: user } = useQuery(['user', userId], () => fetchUser(userId));

const { mutate: sendEmail } = useNotification();
const { mutate: sendSMS } = useNotification();
const { mutate: sendPush } = useNotification();

const sendAlert = async () => {
const promises = [];

// Email
if (user?.email) {
promises.push(
sendEmail({
channel: 'email',
to: user.email,
subject: 'Important Alert',
body: message
})
);
}

// SMS
if (user?.phone) {
promises.push(
sendSMS({
channel: 'sms',
to: user.phone,
template: 'alert-sms',
data: { message }
})
);
}

// Push
if (user?.deviceTokens?.length > 0) {
user.deviceTokens.forEach(token => {
promises.push(
sendPush({
channel: 'push',
to: token,
data: {
title: 'Alert',
body: message,
priority: 'high'
}
})
);
});
}

await Promise.all(promises);
};

return (
<button onClick={sendAlert}>
Send Multi-Channel Alert
</button>
);
}

Next Steps