npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@codingfactory/notify-kit-client

v0.2.2

Published

Headless Vue 3/Nuxt client for Notify Kit notifications

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-client

For 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