@nurdd/web-sdk
@nurdd/web-sdk
Framework-agnostic attribution & analytics SDK for the browser, powered by Nurdd. One package, one integration, every popular frontend — measure where your traffic, signups and revenue actually come from without building a data pipeline.
| Framework | Import path | Helper |
|---|---|---|
| Vanilla JS | <script> UMD or ESM |
Nurdd.initSdk({...}) |
| React | @nurdd/web-sdk/react |
<NurddProvider>, useNurdd() |
| Next.js (App Router) | @nurdd/web-sdk/next |
<NurddProvider>, <NurddPageView /> |
| Vue 3 | @nurdd/web-sdk/vue |
NurddPlugin, useNurdd() |
| Nuxt 3 | @nurdd/web-sdk/nuxt |
plugins/nurdd.client.ts |
| Angular | @nurdd/web-sdk/angular |
provideNurdd(), NurddService |
| Remix / SvelteKit / Solid | core import | createSdk({...}) |
Table of contents
- Features
- Prerequisites
- Getting your API key
- Install
- Quick start
- Configuration
- Framework guides
- Public API
- How it works
- Best practices
- Troubleshooting
- FAQ
- Versioning
- License
Features
- Event tracking with offline buffering and automatic retries
- Session tracking with automatic idle expiry
- Conversion tracking (purchase, signup, custom goals)
- Deep-link attribution, including deferred (post-install) flows
- Campaign & UTM tracking with auto-strip after capture
- Cross-platform identity mapping
- Auto-tracking: SPA page views, outbound clicks, unload-time flush
- SSR-safe: no
windowaccess during server rendering - Zero runtime dependencies
Everything shows up in your Nurdd dashboard, ready to slice by campaign, cohort and conversion.
Prerequisites
- A Nurdd account.
- A public SDK key and your API URL — see Getting your API key.
- A modern browser build target (ES2019+). Framework adapters require the matching framework as a peer dependency (React ≥ 16.8, Vue ≥ 3, Angular ≥ 12).
Getting your API key
API keys are issued to you by a Brand from inside the Nurdd app — you cannot generate them yourself. To receive a key:
- The Brand logs in to the Nurdd app.
- They open Settings → SDK Setup.
- They enter your (the developer's) email address.
- Nurdd shares the API key with you.
Use the key you receive as your sdkKey. If you don't have one yet, ask your
Brand contact to complete the SDK Setup step above.
Install
npm install @nurdd/web-sdk
# or
yarn add @nurdd/web-sdk
# or
pnpm add @nurdd/web-sdk
For frameworkless <script> use, load the prebuilt UMD bundle from the Nurdd
CDN:
<script src="https://cdn.nurdd.club/web-sdk/nurdd-sdk.umd.js"></script>
Quick start
import { initSdk } from "@nurdd/web-sdk";
const sdk = initSdk({
sdkKey: "sdk_pk_xxx", // the key your Brand shared with you
baseUrl: "https://api.nurdd.com", // your Nurdd API URL
});
sdk.track("button_clicked", { button: "hero_cta" });
sdk.identify("user_42", { email: "user@example.com" });
sdk.conversion("purchase", { value: 49.99, currency: "USD" });
Page views, sessions and campaign attribution are tracked automatically from here — see the framework guides for the idiomatic setup in your stack.
Configuration
| Option | Default | What it does |
|---|---|---|
sdkKey (required) |
— | Your public SDK key from the Nurdd dashboard |
baseUrl (required) |
— | Your Nurdd API URL (e.g. https://api.nurdd.com) |
appVersion |
null |
Forwarded with every event for cohort analysis |
debug |
false |
Verbose console output |
flushIntervalMs |
5000 |
Background queue flush cadence |
maxBatchSize |
50 |
Max events per batched request |
sessionTimeoutMs |
1800000 |
Idle-session expiry (30 min) |
autotrack.pageViews |
true |
Wire history navigation to page_view |
autotrack.outboundClicks |
false |
Track cross-origin anchor clicks |
autotrack.flushOnUnload |
true |
Flush pending events on page hide |
stripUtmFromUrl |
true |
Remove UTM params from the URL after first read |
Framework guides
Vanilla JS / HTML
<!doctype html>
<html>
<body>
<script src="https://cdn.nurdd.club/web-sdk/nurdd-sdk.umd.js"></script>
<script>
const sdk = Nurdd.initSdk({
sdkKey: "sdk_pk_xxx",
baseUrl: "https://api.nurdd.com",
});
sdk.track("button_clicked", { button: "hero_cta" });
sdk.identify("user_42", { email: "user@example.com" });
sdk.conversion("purchase", { value: 49.99, currency: "USD" });
sdk.onDeepLink((dl) => {
if (dl.deep_link_path) location.href = dl.deep_link_path;
});
</script>
</body>
</html>
ESM equivalent (with a bundler):
import { initSdk } from "@nurdd/web-sdk";
const sdk = initSdk({ sdkKey: "sdk_pk_xxx", baseUrl: "https://api.nurdd.com" });
sdk.track("page_loaded");
React
// app.tsx
import { NurddProvider } from "@nurdd/web-sdk/react";
export function App() {
return (
<NurddProvider
config={{ sdkKey: "sdk_pk_xxx", baseUrl: "https://api.nurdd.com" }}
>
<Routes />
</NurddProvider>
);
}
// any component
import { useTrack, useIdentity, useDeepLink } from "@nurdd/web-sdk/react";
function CheckoutButton({ priceId }) {
const track = useTrack();
return (
<button onClick={() => track("checkout_started", { priceId })}>Buy</button>
);
}
function Boot() {
const { identify } = useIdentity();
useDeepLink((dl) => {
if (dl.deep_link_path) navigate(dl.deep_link_path);
});
useEffect(() => {
identify("user_42", { email: "user@example.com" });
}, []);
return null;
}
Next.js (App Router)
// app/providers.tsx — Client Component
"use client";
import { NurddProvider } from "@nurdd/web-sdk/next";
export default function Providers({ children }) {
return (
<NurddProvider
config={{ sdkKey: "sdk_pk_xxx", baseUrl: "https://api.nurdd.com" }}
>
{children}
</NurddProvider>
);
}
// app/page-view.tsx
("use client");
import { usePathname, useSearchParams } from "next/navigation";
import { NurddPageView } from "@nurdd/web-sdk/next";
export default function PageView() {
return (
<NurddPageView pathname={usePathname()} searchParams={useSearchParams()} />
);
}
// app/layout.tsx
import Providers from "./providers";
import PageView from "./page-view";
export default function RootLayout({ children }) {
return (
<html>
<body>
<Providers>
<PageView />
{children}
</Providers>
</body>
</html>
);
}
Vue 3
// main.ts
import { createApp } from "vue";
import { NurddPlugin } from "@nurdd/web-sdk/vue";
import App from "./App.vue";
createApp(App)
.use(NurddPlugin, {
sdkKey: "sdk_pk_xxx",
baseUrl: "https://api.nurdd.com",
})
.mount("#app");
<!-- any component -->
<script setup>
import { useRoute } from "vue-router";
import { useNurdd, trackPageViews, useIdentity } from "@nurdd/web-sdk/vue";
const sdk = useNurdd();
trackPageViews(useRoute());
const { identify } = useIdentity();
function buy() {
sdk.conversion("purchase", { value: 19.0, currency: "USD" });
}
</script>
Nuxt 3
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
public: {
nurdd: { sdkKey: "sdk_pk_xxx", baseUrl: "https://api.nurdd.com" },
},
},
});
// plugins/nurdd.client.ts
export { default } from "@nurdd/web-sdk/nuxt";
<!-- any component -->
<script setup>
const { $nurdd } = useNuxtApp();
$nurdd.track("hero_viewed");
</script>
Angular (standalone)
// app.config.ts
import { ApplicationConfig } from "@angular/core";
import { provideNurdd } from "@nurdd/web-sdk/angular";
export const appConfig: ApplicationConfig = {
providers: [
provideNurdd({ sdkKey: "sdk_pk_xxx", baseUrl: "https://api.nurdd.com" }),
],
};
// any.component.ts
import { Component, inject } from "@angular/core";
import { NurddService } from "@nurdd/web-sdk/angular";
@Component({
selector: "buy-btn",
template: `<button (click)="buy()">Buy</button>`,
})
export class BuyButton {
private nurdd = inject(NurddService);
buy() {
this.nurdd.conversion("purchase", { value: 9.99, currency: "USD" });
}
}
Remix / SvelteKit / Solid
The core works anywhere — import createSdk and store the instance somewhere
global (a Remix loader context, a Svelte store, a Solid signal, etc.). Every
server-only render is a no-op.
import { createSdk } from "@nurdd/web-sdk";
export const nurdd = createSdk({
sdkKey: "sdk_pk_xxx",
baseUrl: "https://api.nurdd.com",
});
if (typeof window !== "undefined") nurdd.init();
Public API
sdk.init(); // idempotent; safe to call anytime
sdk.track(eventName, properties?, options?);
sdk.page(name?, properties?);
sdk.identify(externalUserId, traits?);
sdk.reset(); // clear the current user on logout
sdk.conversion(type, { value?, currency?, campaignId?, attribution? });
sdk.onDeepLink(listener); // returns an unsubscribe fn
sdk.flush(); // deliver pending events now
sdk.endSession();
sdk.destroy();
sdk.info(); // diagnostics
Every method is safe to call before init() resolves — events are queued locally
and shipped on the next flush.
React / Vue hooks
useNurdd(); // the SDK instance
useTrack(); // (event, properties?) => void
useIdentity(); // { identify(userId, traits?), reset() }
useDeepLink(cb); // run a callback when the user arrives from a campaign link
How it works
The SDK is offline-first and designed to never lose an event:
- Events are persisted locally before the network call, so a tab crash or a dropped connection can't lose them — they retry automatically on reconnect.
- On page hide, pending events are flushed reliably so last-mile events still ship.
- Attribution captured from a landing URL (UTM/campaign) is remembered and stamped onto later conversions, so a signup or purchase still credits the original campaign even after the URL is cleaned up.
Best practices
- Initialise once near the root of your app and reuse the single instance.
- Call
identify()on login/signup so anonymous activity is stitched to the known user, andreset()on logout. - Let attribution flow automatically — record a
conversion()and the SDK credits the campaign the visitor arrived from, with no extra wiring. - Keep only your public key in the browser. The public key is safe to ship; never embed a server key in client code.
- Add
flushOnUnload-friendly events rather than blocking navigation to send data — the SDK already handles unload flushing.
Troubleshooting
| Symptom | Likely cause & fix |
|---|---|
sdkKey is required / baseUrl is required |
Pass both sdkKey and baseUrl to initSdk/createSdk. |
| Nothing appears in the dashboard | Enable debug: true and check the console; confirm the key and API URL. |
| CORS / origin errors in the browser | Add your site's origin to the key's allow-list in the dashboard. |
| Conversions not attributed to a campaign | Ensure the visitor arrived via a Nurdd campaign link before converting. |
| Duplicate page views in an SPA | Use the framework's page-view helper instead of tracking manually. |
| Events delayed | This is expected — events batch and flush every flushIntervalMs; call sdk.flush() to force delivery. |
FAQ
Is the SDK safe to render on the server?
Yes. It never touches window during SSR; browser-only work is deferred to the
client.
Does it add runtime dependencies? No. The core has zero runtime dependencies. Framework adapters use the framework you already have as an optional peer dependency.
Can I run more than one instance?
Yes. Use createSdk() for isolated instances (e.g. multi-tenant), or initSdk()
for the common single-instance case.
Where do I get my key and API URL? Your key is issued to you by a Brand from the Nurdd app — see Getting your API key. You cannot generate one yourself.
Versioning
This package follows semantic versioning. The SDK talks to a versioned Nurdd API, so a new major version of the SDK can adopt a new API version while existing apps keep working unchanged. Pin a major version and upgrade deliberately.
Related Packages
Multi-platform analytics SDK for browser, React, Next.js, Remix, Vue, Svelte, Nu...
Browser tracking SDK for Outlit customer context with React and Vue bindings
Browser client for Loomail: identify people and track events from any web app, w...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 2.0.0 | >=3.0.0 |