@codingfactory/notify-kit-client
v0.2.2
Published
Headless Vue 3/Nuxt client for Notify Kit notifications
Maintainers
Readme
@coding-factory/notify-kit-client
Headless Vue 3/Nuxt client for Notify Kit notifications.
Features
- TypeScript-first - Strict types matching the backend payload contract
- HTTP Client - Full API coverage with type-safe methods
- Vue 3 Composables - Headless, reactive state management
- Nuxt Module - SSR-safe plugin with auto-imports
- Real-time Integration - Laravel Echo subscription handling
- Modal Queue Management - Built-in critical notification modals
- Toast Policy Helpers - Decide what toasts and plays sounds
Ownership
@coding-factory/notify-kit-client is the package-owned home for generic frontend notification behavior.
- Put typed notification API calls, unread-count refresh logic, inbox pagination, realtime merge rules, modal queue helpers, and preference-settings access here.
- Keep consumer apps thin: they should mostly provide rendering, routing, branded presentation, and app-specific side effects.
- Do not add a second app-local notification store or duplicate realtime subscription pipeline when the behavior can live in this package.
Installation
npm install @coding-factory/notify-kit-clientFor Nuxt applications, no additional setup is needed for the module.
Vue 3 Setup
1. Create and Provide the Client
// main.ts
import { createApp, provide } from 'vue';
import {
createNotifyKitClient,
NOTIFY_KIT_CLIENT_KEY,
} from '@coding-factory/notify-kit-client';
import App from './App.vue';
const client = createNotifyKitClient({
baseUrl: '/api/notify-kit',
// Optional: provide auth headers
getAuthHeaders: () => ({
Authorization: `Bearer ${getToken()}`,
}),
});
const app = createApp(App);
app.provide(NOTIFY_KIT_CLIENT_KEY, client);
app.mount('#app');2. Use Composables in Components
<script setup lang="ts">
import {
useNotifyKitClient,
useNotifyKitStore,
useNotifyKitModals,
} from '@coding-factory/notify-kit-client';
// Access the client for API calls
const { client } = useNotifyKitClient();
// Access reactive store state
const store = useNotifyKitStore();
const unreadCount = computed(() => store.state.unreadCount);
// Access modal queue
const { currentModal, hasModals, ack, snooze } = useNotifyKitModals();
</script>3. Set Up Real-time with Echo
// composables/useNotifications.ts
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
import {
useNotifyKitClient,
useNotifyKitRealtime,
useNotifyKitStore,
defaultToastPolicy,
} from '@coding-factory/notify-kit-client';
// Configure Echo (one-time setup)
window.Pusher = Pusher;
const echo = new Echo({
broadcaster: 'reverb',
key: import.meta.env.VITE_REVERB_APP_KEY,
wsHost: import.meta.env.VITE_REVERB_HOST,
wsPort: import.meta.env.VITE_REVERB_PORT,
forceTLS: false,
enabledTransports: ['ws'],
});
export function useNotifications() {
const { client } = useNotifyKitClient();
const store = useNotifyKitStore();
const { connect, disconnect, isConnected } = useNotifyKitRealtime({
echo: () => echo,
autoConnect: true,
onNotification: (notification) => {
// Handle new notification
if (defaultToastPolicy.shouldToast(notification)) {
showToast(notification);
}
},
});
return {
connect,
disconnect,
isConnected,
unreadCount: computed(() => store.state.unreadCount),
};
}Nuxt 3 Setup
1. Add the Module
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@coding-factory/notify-kit-client/nuxt'],
notifyKit: {
baseUrl: '/api/notify-kit',
autoConnect: true,
},
// Proxy API to Laravel backend
routeRules: {
'/api/**': { proxy: 'http://localhost:8000/api/**' },
},
});2. Use the Auto-imported Composable
<script setup lang="ts">
// useNotifyKitNuxt is auto-imported
const { client, state, fetchUnreadCount } = useNotifyKitNuxt();
onMounted(async () => {
await fetchUnreadCount();
});
</script>
<template>
<div>
<span>{{ state.unreadCount }} unread</span>
</div>
</template>SSR Considerations
The Nuxt plugin handles SSR safety automatically. For components that use browser APIs (like Echo), wrap them in <ClientOnly>:
<template>
<ClientOnly>
<ModalManager />
<ToastContainer />
</ClientOnly>
</template>Composables Reference
useNotifyKitClient
Access the HTTP client for API calls.
const { client } = useNotifyKitClient();
// API methods
await client.listNotifications({ unread: true, per_page: 10 });
await client.getUnreadCount();
await client.markRead(notificationId);
await client.markAllRead();
await client.deleteNotification(notificationId);
await client.listModals();
await client.ackModal(modalId);
await client.snoozeModal(modalId, 15); // minutes
await client.getPreferences();
await client.updatePreferences({ categories: { ... } });useNotifyKitStore
Centralized reactive state for notifications.
const store = useNotifyKitStore();
// Reactive state
store.state.notifications; // NotifyKitNotification[]
store.state.unreadCount; // number
store.state.modalQueue; // NotifyKitNotification[]
store.state.connectionState; // 'disconnected' | 'connecting' | 'connected' | 'error'
store.state.broadcastChannel; // string | null
// Computed helpers
store.hasUnread; // ComputedRef<boolean>
store.currentModal; // ComputedRef<NotifyKitNotification | null>
store.hasModals; // ComputedRef<boolean>
// Actions
store.addNotification(notification);
store.markNotificationRead(id);
store.removeNotification(id);
store.incrementUnreadCount();
store.decrementUnreadCount();
store.enqueueModal(notification);
store.dequeueModal(id);
store.setConnectionState(state);
store.setBroadcastChannel(channel);
store.reset();useNotifyKitRealtime
Real-time subscription management via Laravel Echo.
const {
isConnected,
connectionState,
connect,
disconnect,
reconnect,
} = useNotifyKitRealtime({
echo: () => echoInstance,
autoConnect: true,
onNotification: (notification) => {
// Called when notification received
},
onConnectionChange: (state) => {
// Called when connection state changes
},
});useNotifyKitModals
Modal queue management for critical notifications.
const {
currentModal, // Current modal to display (first in queue)
modalQueue, // All queued modals
hasModals, // Are there modals to show?
isLoading, // Loading outstanding modals?
loadOutstandingModals, // Fetch from API on login/refresh
ack, // Acknowledge a modal
snooze, // Snooze a modal
dismissCurrent, // Ack current modal
snoozeCurrent, // Snooze current modal
getSnoozeOptions, // Get allowed snooze durations
} = useNotifyKitModals();
// Acknowledge
await ack(notificationId);
// Snooze for 60 minutes
await snooze(notificationId, 60);
// Get snooze options from modal spec
const options = getSnoozeOptions(notification);
// [15, 60, 240, 1440]Toast Policy Helpers
Decide which notifications should display as toasts and play sounds.
import {
defaultToastPolicy,
createToastPolicy,
} from '@coding-factory/notify-kit-client';
// Default policy
// - Critical modals (requires_ack): no toast (modal is the UX)
// - Social category: no toast (too frequent)
// - Security category: always toast + play sound
// - Danger level: play sound
// - Everything else: toast, no sound
if (defaultToastPolicy.shouldToast(notification)) {
showToast(notification);
}
if (defaultToastPolicy.shouldPlaySound(notification)) {
playSound();
}
// Custom policy
const customPolicy = createToastPolicy({
// Enable toasts for social
toastCategories: {
social: true,
},
// Play sound for warnings too
soundLevels: ['warning', 'danger'],
// Play sound for orders
soundCategories: ['security', 'orders'],
});TypeScript Types
All types are exported for use in your application.
import type {
// Core notification types
NotifyKitNotification,
NotificationCategory,
NotificationLevel,
NotifyKitModalSpec,
NotifyKitModalState,
// API response types
NotifyKitMeResponse,
NotifyKitListResponse,
NotifyKitGroupedEntry,
NotifyKitUnreadCountResponse,
NotifyKitPreferences,
PaginationMeta,
// Client configuration
NotifyKitClientConfig,
} from '@coding-factory/notify-kit-client';Type Guards
Validate data at runtime:
import {
isNotifyKitNotification,
isModalEnabled,
isValidCategory,
isValidLevel,
isPaginationMeta,
isMeResponse,
isUnreadCountResponse,
isModalSpec,
isChannelPreferences,
} from '@coding-factory/notify-kit-client';
// Validate incoming notification from Echo
if (isNotifyKitNotification(data)) {
store.addNotification(data);
}
// Check if notification has active modal
if (isModalEnabled(notification)) {
store.enqueueModal(notification);
}Recommended UX Patterns
Notification Bell
<script setup lang="ts">
const { client, state } = useNotifyKitNuxt();
async function loadNotifications() {
const response = await client.listNotifications({ per_page: 10 });
state.notifications = response.data;
}
async function markAsRead(id: string) {
await client.markRead(id);
// Optimistically update UI
const notification = state.notifications.find(n => n.id === id);
if (notification) notification.read_at = new Date().toISOString();
state.unreadCount--;
}
</script>
<template>
<div class="bell">
<button @click="toggle">
🔔
<span v-if="state.unreadCount > 0" class="badge">
{{ state.unreadCount }}
</span>
</button>
<!-- Dropdown with notification list -->
</div>
</template>Modal Manager
<script setup lang="ts">
const { currentModal, hasModals, ack, snooze, getSnoozeOptions } = useNotifyKitModals();
async function handleAck() {
if (currentModal.value) {
await ack(currentModal.value.id);
}
}
async function handleSnooze(minutes: number) {
if (currentModal.value) {
await snooze(currentModal.value.id, minutes);
}
}
</script>
<template>
<Teleport to="body">
<div v-if="hasModals && currentModal" class="modal-overlay">
<div class="modal">
<h2>{{ currentModal.title }}</h2>
<p>{{ currentModal.body }}</p>
<div class="actions">
<select @change="handleSnooze(Number($event.target.value))">
<option disabled selected>Remind me later</option>
<option v-for="min in getSnoozeOptions(currentModal)" :value="min">
{{ formatMinutes(min) }}
</option>
</select>
<button @click="handleAck">Acknowledge</button>
</div>
</div>
</div>
</Teleport>
</template>Examples
See the examples directory for complete working examples:
- Vue 3 App -
examples/vue-app/ - Nuxt 3 App -
examples/nuxt-app/
API Reference
Client Configuration
interface NotifyKitClientConfig {
/** Base URL for the API (e.g., '/api/notify-kit') */
baseUrl: string;
/** Optional function to provide auth headers */
getAuthHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
/** Optional custom fetch function */
fetch?: typeof fetch;
}Module Options (Nuxt)
interface NotifyKitModuleOptions {
/** Base URL for the API (default: '/api/notify-kit') */
baseUrl?: string;
/** Auto-connect to real-time on client (default: true) */
autoConnect?: boolean;
}Requirements
- Vue 3.4+
- TypeScript 5.0+
- For real-time: Laravel Echo + Pusher.js (or Reverb)
- Backend: notify-kit Laravel package
License
MIT
