Imported from github.com/cyguin/notify
Find a file
2026-05-16 14:36:07 -07:00
src gsd: pre-implementation lint/format cleanup 2026-05-16 14:36:07 -07:00
.gitignore gsd: pre-implementation lint/format cleanup 2026-05-16 14:36:07 -07:00
DEV_LOG.md gsd: pre-implementation lint/format cleanup 2026-05-16 14:36:07 -07:00
LICENSE chore: normalize cyguin package metadata 2026-04-21 09:26:39 -05:00
package-lock.json docs: align notify dark theme docs 2026-04-21 09:32:43 -05:00
package.json docs: align notify dark theme docs 2026-04-21 09:32:43 -05:00
README.md gsd: pre-implementation lint/format cleanup 2026-05-16 14:36:07 -07:00
tsconfig.json chore: initial publish 2026-04-05 08:10:33 -05:00
tsup.config.ts gsd: pre-implementation lint/format cleanup 2026-05-16 14:36:07 -07:00

@cyguin/notify

Server-triggered in-app notifications for Next.js apps.

Install

npm install @cyguin/notify

Setup

1. Create the API route

Create app/api/notify/[...cyguin]/route.ts in your Next.js app:

import { createNotifyHandler } from "@cyguin/notify/next";
import Database from "better-sqlite3";
import { createSQLiteAdapter } from "@cyguin/notify/adapters/sqlite";

const db = new Database("notify.db");
db.pragma("journal_mode = WAL");
const adapter = createSQLiteAdapter(db);

const handler = createNotifyHandler({
  adapter,
  secret: process.env.NOTIFY_SECRET,
});

export { handler as GET, handler as POST, handler as PATCH };

2. Run migrations

CREATE TABLE notifications (
  id          TEXT PRIMARY KEY,
  user_id     TEXT NOT NULL,
  title       TEXT NOT NULL,
  body        TEXT NOT NULL,
  href        TEXT,
  read_at     INTEGER,
  created_at  INTEGER NOT NULL
);

3. Configure the adapter at startup

In your app startup (e.g., a setup file or server entry):

import { setNotificationAdapter } from "@cyguin/notify";
import { createSQLiteAdapter } from "@cyguin/notify/adapters/sqlite";

const adapter = createSQLiteAdapter(db);
setNotificationAdapter(adapter);

4. Set the internal API secret

NOTIFY_SECRET=change-me

POST is an internal mutation route and requires Authorization: Bearer $NOTIFY_SECRET. GET and PATCH stay callable by the widget; pair the route with your app's own user/session boundary so users can only read and update their own notifications.

5. Trigger notifications from your server code

import { notify } from "@cyguin/notify";

// In a server action, API route, or background job:
await notify(userId, {
  title: "Export ready",
  body: "Your data export is ready to download.",
  href: "/exports/123",
});

6. Add the notification bell

import { NotificationBell } from "@cyguin/notify/react";

export default function Header({ user }: { user: { id: string } }) {
  return (
    <header>
      <nav>...</nav>
      <NotificationBell
        userId={user.id}
        theme="dark"
        pollInterval={30000}
        maxVisible={10}
        onToggle={(open) => console.log("Dropdown:", open)}
      />
    </header>
  );
}

API Routes

Method Route Description
GET /api/notify?userId=xxx&limit=N List notifications for user
POST /api/notify Create notification (internal, requires Bearer token)
PATCH /api/notify/:id/read?userId=xxx Mark notification as read for the widget user

NotificationBell Props

Prop Default Description
userId — User ID to fetch notifications for.
theme 'dark' Visual theme. Pass 'light' to opt into the light theme.
pollInterval 30000 Polling interval in ms. Pass 0 to disable.
maxVisible 10 Max notifications to show in dropdown.
className '' CSS class for the root element.
onToggle — Callback when dropdown opens/closes.

Theming

NotificationBell defaults to the cyguin dark theme. Use --cyguin-* CSS custom properties on .cyguin-notify-bell:

.cyguin-notify-bell {
  --cyguin-bg: #ffffff;
  --cyguin-fg: #0a0a0a;
  --cyguin-accent: #f5a800;
  --cyguin-border: #e5e5e5;
  --cyguin-radius: 6px;
  --cyguin-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
}

The dark theme applies these overrides automatically:

.cyguin-notify-bell[data-theme="dark"] {
  --cyguin-bg: #0a0a0a;
  --cyguin-bg-subtle: #1a1a1a;
  --cyguin-border: #2a2a2a;
  --cyguin-fg: #f5f5f5;
  --cyguin-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}

Postgres Setup

import { createNotifyHandler } from "@cyguin/notify/next";
import postgres from "postgres";
import { createPostgresAdapter } from "@cyguin/notify/adapters/postgres";

const sql = postgres(process.env.DATABASE_URL!);
const adapter = createPostgresAdapter(sql);

const handler = createNotifyHandler({
  adapter,
  secret: process.env.NOTIFY_SECRET,
});

export { handler as GET, handler as POST, handler as PATCH };

License

MIT