Elaan JavaScript SDKs — notification inbox, preferences & push for React and React Native (core + framework bindings).
git clone https://github.com/ThingsIDoForLove/elaan-jsTools overview
# Elaan JavaScript SDKs
Client SDKs for [Elaan](https://elaan.io) — drop-in notification inbox,
preferences, and push-token management for your users' frontends.
This is the **JavaScript/TypeScript** family. Swift and Kotlin/Android live in
their own repos (`elaan-swift`, `elaan-kotlin`).
## Packages
| Package | What it is | Registry |
|---|---|---|
| [`@elaanio/core`](./core) | Framework-agnostic foundation — API client, types, observable inbox/preferences stores, and a realtime-transport interface. No UI, no framework. | npm |
| [`@elaanio/react-core`](./react-core) | React bindings only (no DOM) — `ElaanProvider` + hooks (`useNotifications`, `useUnreadCount`, `usePreferences`, `usePush`) over the core stores. Shared by web and native. | npm |
| [`@elaanio/react`](./react) | React (web) components — notification bell, feed, and preferences UI, plus real-time updates over SSE-on-fetch. | npm |
| [`@elaanio/react-native`](./react-native) | React Native components over the same hooks; realtime over SSE (`react-native-sse`) with polling fallback. | npm |
| [`@elaanio/vue`](./vue) | Vue 3 components + composables — bell, feed, and preferences, with fetch-SSE realtime. | npm |
| [`@elaanio/svelte`](./svelte) | Svelte stores (headless) — reactive inbox, unread count, and preferences; bring your own markup. | npm |
| [`@elaanio/elements`](./elements) | Framework-agnostic Web Components — `<elaan-bell>` / `<elaan-feed>` / `<elaan-preferences>`. Drop into any page or framework. | npm |
### How they fit together
```
@elaanio/core vanilla TS: client · stores · realtime transport
├─ @elaanio/react-core React provider + hooks (framework, no DOM)
│ ├─ @elaanio/react web components + fetch-SSE realtime
│ └─ @elaanio/react-native RN components (SSE via react-native-sse)
├─ @elaanio/vue Vue 3 components + composables
├─ @elaanio/svelte Svelte stores (headless)
└─ @elaanio/elements Web Components (works anywhere)
```
The non-visual logic lives once in `@elaanio/core`; each framework package is a
thin adapter over the core client + observable stores (React goes through the
shared `@elaanio/react-core` hooks; Vue and Svelte bind the stores to their own
reactivity). Adding another framework means a new binding over `@elaanio/core`,
not a reimplementation of the client or stores.
## Authentication (all packages)
The SDK never sees your API key. Your backend mints a short-lived **contact
token** for the signed-in user (`POST /v1/contacts/tokens` with your service
key, by `external_id`) and returns it to the client. You pass the SDK a
`tokenProvider` callback that fetches a fresh token from your own endpoint; it
refreshes automatically on expiry. See [`@elaanio/react`](./react#readme) for the
full token flow.
## Quick start — React (web)
```tsx
import { ElaanProvider, NotificationBell, Preferences } from "@elaanio/react";
import "@elaanio/react/styles.css";
async function tokenProvider() {
const res = await fetch("/api/elaan-token"); // your endpoint
const { token, contact_id } = await res.json();
return { token, contactId: contact_id };
}
export function App() {
return (
<ElaanProvider apiBase="https://api.elaan.io/v1" tokenProvider={tokenProvider}>
<NotificationBell />
<Preferences />
</ElaanProvider>
);
}
```
## Quick start — React Native
Same provider and hooks; the components render with React Native primitives
(`View`/`FlatList`/`Switch`/`Modal`) instead of DOM, and there's no stylesheet
to import. Realtime works over SSE via [`react-native-sse`](https://www.npmjs.com/package/react-native-sse)
(RN can't stream `fetch`, but its XHR-based EventSource can send the auth
header), wired into the RN provider by default and falling back to polling when
the deployment has realtime off. Pass `realtime={null}` for polling only.
```tsx
import {
ElaanProvider,
NotificationBell,
NotificationFeed,
Preferences,
} from "@elaanio/react-native";
async function tokenProvider() {
const res = await fetch("https://yourapp.com/api/elaan-token", {
headers: { Authorization: `Bearer ${yourSessionToken}` },
});
const { token, contact_id } = await res.json();
return { token, contactId: contact_id };
}
export default function App() {
return (
<ElaanProvider apiBase="https://api.elaan.io/v1" tokenProvider={tokenProvider}>
{/* A bell + badge that opens the inbox in a modal card */}
<NotificationBell />
{/* …or the inbox inline as a full screen */}
<NotificationFeed emptyText="Nothing here yet." />
{/* per-type × channel preference switches */}
<Preferences />
</ElaanProvider>
);
}
```
### Registering a device token (Expo / FCM)
```tsx
import { usePush } from "@elaanio/react-native";
function useRegisterPush(expoToken: string) {
const { register } = usePush();
useEffect(() => {
// provider: "expo" | "fcm" | "apns" | "onesignal" | "webpush"
register(expoToken, "expo", "ios");
}, [expoToken]);
}
```
## Theming & styling
**Web (`@elaanio/react`)** ships a stylesheet driven entirely by CSS variables, so
you theme it without touching component internals. Import the stylesheet once,
then override the variables on `:root` (or any ancestor of the components):
```css
:root {
--elaan-accent: #7c3aed; /* brand color: badges, links, active states */
--elaan-accent-ink: #fff; /* text/icon on top of the accent */
--elaan-bg: #ffffff;
--elaan-bg-hover: #f4f6f9;
--elaan-text: #1a1d23;
--elaan-muted: #6b7280;
--elaan-border: #e5e7eb;
--elaan-danger: #ef4444;
--elaan-radius: 10px;
--elaan-shadow: 0 12px 32px -12px rgba(0, 0, 0, 0.3);
}
```
The default theme already adapts to light/dark via `prefers-color-scheme`;
override the same variables inside your own media query to customize dark mode.
Each element also carries a stable `elaan-*` class (`.elaan-bell`,
`.elaan-feed`, `.elaan-item`, …) if you need finer CSS control.
**React Native (`@elaanio/react-native`)** components use `StyleSheet` with a
small built-in palette (accent, background, text, muted, border). There are no
CSS variables in RN, so for anything beyond the defaults — brand fonts, custom
row layouts, dark-mode palettes — build your own components on the hooks (next
section). That's the intended path for heavy RN customization.
## Building your own components
The packaged components are deliberately thin. When the defaults don't fit —
your own markup, a design system, a different layout, or a framework we don't
ship yet — drop down a layer. Pick the lowest one you need:
**1. Same framework, your own UI → use the hooks.** `@elaanio/react` and
`@elaanio/react-native` both re-export the hooks from `@elaanio/react-core`. Render
whatever you like; the hook owns loading, polling, realtime, and optimistic
updates:
```tsx
import { useNotifications, usePreferences } from "@elaanio/react"; // or /react-native
function MyInbox() {
const { notifications, unreadCount, loading, markRead, markAllRead, remove } =
useNotifications();
if (loading && notifications.length === 0) return <Spinner />;
return (
<MyList
items={notifications}
onOpen={(n) => markRead(n.id)}
onDismiss={(n) => remove(n.id)}
onClearAll={markAllRead}
/>
);
}
function MyPrefs() {
const { preferences, setPreference, clearPreference } = usePreferences();
// preferences: [{ notification_type_key, channels: [{ channel, enabled, overridden }] }]
// setPreference(typeKey, channel, enabled) / clearPreference(typeKey, channel)
}
```
Available hooks: `useNotifications`, `useUnreadCount`, `usePreferences`,
`usePush` — all require an ancestor `<ElaanProvider>`.
**2. A different framework (Vue, Svelte, Solid, vanilla) → use `@elaanio/core`.**
The core exposes the same logic as framework-agnostic observable stores. This is
exactly what `@elaanio/react-core` is built on, so a new binding is small:
```ts
import { ElaanClient, createInboxStore } from "@elaanio/core";
const client = new ElaanClient("https://api.elaan.io/v1", tokenProvider);
const inbox = createInboxStore(client, { pollInterval: 30000 });
inbox.subscribe(() => render(inbox.getState())); // getState() / getUnreadCount()
inbox.markRead(id); // + markUnread / markAllRead / remove / refresh
// inbox.destroy() when you tear down
```
Wire `store.subscribe` + `store.getState` into your framework's reactivity
(Vue `ref`, Svelte store contract, `useSyncExternalStore`, …). For one-off calls
that don't need a store, `ElaanClient` has every endpoint directly. If you build
a binding for another framework, a PR adding an `@elaanio/<framework>` package is
very welcome.
## Development
This repo is a [pnpm](https://pnpm.io) workspace.
```bash
pnpm install # install all packages
pnpm -r build # build every package (topological order)
pnpm -r typecheck
```
## Releasing
Versioning is **manual semver, per package**. To cut a release:
1. Bump `"version"` in the `package.json` of each package you're releasing.
2. Commit, then push a tag:
```bash
git tag v0.2.0 && git push origin v0.2.0
```
3. The [`Publish`](./.github/workflows/publish.yml) workflow builds, typechecks,
and runs `pnpm -r publish` — which publishes each package in dependency order
(rewriting `workspace:*` to real versions) and **skips any version already on
npm**, so releasing a subset just works.
One-time setup (repo owner):
- Own the **`@elaan` scope/org** on [npmjs.com](https://www.npmjs.com/) (the
packages are scoped `@elaanio/*`).
- Add an **`NPM_TOKEN`** repository secret (Settings → Secrets and variables →
Actions) — an npm **Automation** token with publish rights to the scope.
Packages publish as public via each one's `publishConfig.access`, with npm
provenance attested from the workflow.
## License
MIT — see [LICENSE](./LICENSE).
What people ask about elaan-js
What is ThingsIDoForLove/elaan-js?
+
ThingsIDoForLove/elaan-js is tools for the Claude AI ecosystem. Elaan JavaScript SDKs — notification inbox, preferences & push for React and React Native (core + framework bindings). It has 0 GitHub stars and was last updated today.
How do I install elaan-js?
+
You can install elaan-js by cloning the repository (https://github.com/ThingsIDoForLove/elaan-js) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is ThingsIDoForLove/elaan-js safe to use?
+
ThingsIDoForLove/elaan-js has not been audited yet by our security agent. Review the original repository on GitHub before using it in production.
Who maintains ThingsIDoForLove/elaan-js?
+
ThingsIDoForLove/elaan-js is maintained by ThingsIDoForLove. The last recorded GitHub activity is from today, with 0 open issues.
Are there alternatives to elaan-js?
+
Yes. On ClaudeWave you can browse similar tools at /categories/tools, sorted by popularity or recent activity.
Deploy elaan-js to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/thingsidoforlove-elaan-js)<a href="https://claudewave.com/repo/thingsidoforlove-elaan-js"><img src="https://claudewave.com/api/badge/thingsidoforlove-elaan-js" alt="Featured on ClaudeWave: ThingsIDoForLove/elaan-js" width="320" height="64" /></a>More Tools
A single CLAUDE.md file to improve Claude Code behavior, derived from Andrej Karpathy's observations on LLM coding pitfalls.
An AI SKILL that provide design intelligence for building professional UI/UX multiple platforms
🪨 why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman
CLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies
The fastest, litest AI Gateway. Rust core with Python SDK. Call 100+ LLM APIs in OpenAI (or native) format with cost tracking, guardrails, load balancing, and logging [Bedrock, Azure, OpenAI, Anthropic, OpenAI, VertexAI, vLLM, Nvidia NIM]
A collection of notebooks/recipes showcasing some fun and effective ways of using Claude.