In-App Notifications for Next.js Supabase
Add real-time in-app notifications to your SaaS. Database-backed storage, Row Level Security, dismissible alerts, and Supabase Realtime support.
In-app notifications are database-backed alerts displayed in your application's notification center. Unlike email or push notifications, they appear within your UI and persist until dismissed or expired.
MakerKit includes a production-ready notification system: database persistence, Row Level Security, real-time updates via Supabase Realtime, and a pre-built UI component. Notifications work for both personal accounts and team accounts out of the box.
When to use in-app notifications:
- User needs timely alerts without email fatigue
- Notifications tie directly to in-app actions (new comment, export ready)
- You want instant delivery with real-time enabled
When to prefer email instead:
- Time-critical alerts the user might miss if not logged in
- You need delivery confirmation
- Legal or compliance notifications requiring an audit trail
How it works
Notifications flow through three layers:
| Layer | Purpose | Package/Location |
|---|---|---|
| Database | Persistent storage with RLS | public.notifications table |
| Server API | Create notifications from backend code | @kit/notifications/api |
| Client UI | Display and dismiss notifications | @kit/notifications/components |
Only the service_role can insert notifications. This is intentional: notifications should be triggered by your application logic, not by end users directly.
Notification types
Three severity levels, each with distinct visual styling:
- info (default): Blue icon. General updates, welcome messages, feature announcements
- warning: Yellow icon. Attention needed, subscription reminders, approaching limits
- error: Red icon. Payment failures, system errors, action required
Quick example
Send a notification from a Server Action:
import { createNotificationsApi } from '@kit/notifications/api';import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';export async function notifyUser(accountId: string) { const client = getSupabaseServerAdminClient(); const api = createNotificationsApi(client); await api.createNotification({ account_id: accountId, body: 'Your export is ready to download', link: '/exports', type: 'info', });}The notification appears in the user's bell icon dropdown. If real-time is enabled, it appears instantly without a page refresh.
Feature flags
Control notifications via environment variables:
# Show/hide the bell icon entirelyNEXT_PUBLIC_ENABLE_NOTIFICATIONS=true# Enable Supabase Realtime subscriptions (adds cost)NEXT_PUBLIC_REALTIME_NOTIFICATIONS=falseReal-time is disabled by default. Each connected client maintains a Supabase Realtime subscription, which counts toward your Supabase plan limits. For most apps, polling on page load is sufficient.
Personal vs team notifications
The account_id field determines who sees the notification:
- Personal account ID (user's UUID): Only that user sees it
- Team account ID: All team members see it
// Notify a single userawait api.createNotification({ account_id: userId, body: 'Welcome to the platform!',});// Notify an entire teamawait api.createNotification({ account_id: teamAccountId, body: 'New team member joined', link: '/settings/members',});RLS policies handle visibility automatically. Users see notifications for their personal account plus any team accounts they belong to.
Common pitfalls
Using the wrong Supabase client: Notifications require getSupabaseServerAdminClient(), not the regular server client. Only service_role can insert into the notifications table.
Enabling real-time without checking plan limits: Each connected client maintains a WebSocket connection. Free tier allows 200 concurrent connections. Calculate your expected concurrent users before enabling.
Hardcoding messages instead of translation keys: For i18n support, store translation keys like 'notifications:exportReady' instead of plain text.
Forgetting expiration for time-sensitive alerts: Default expiration is 1 month. Set a shorter expires_at for flash sales, limited-time offers, or ephemeral events.
Frequently Asked Questions
Can I send notifications from client-side code?
How do I send notifications to all team members?
Do notifications count toward Supabase database limits?
How do I delete old notifications?
Can users edit or delete their own notifications?
Documentation
- Configuration: Environment variables and feature flags
- Sending Notifications: Server-side API with examples
- UI Components: NotificationsPopover and custom implementations
- Database Schema: Table structure, RLS policies, and indexes