React & Next.js SDK
Full reference for @onramp-sdk/react. Works with React 18+, Next.js App Router, and Next.js Pages Router.
Installation
npm install @onramp-sdk/react
Setup
Next.js App Router
Add OnRampProvider to your root layout. It initializes once on the client and is SSR-safe - it no-ops on the server.
// app/layout.tsx
import { OnRampProvider } from '@onramp-sdk/react'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<OnRampProvider apiKey="onr_xxxxxxxxxxxx" appVersion="2.4.1">
{children}
</OnRampProvider>
</body>
</html>
)
}
React (Vite, CRA, etc.)
// main.tsx
import { createRoot } from 'react-dom/client'
import { OnRampProvider } from '@onramp-sdk/react'
import App from './App'
createRoot(document.getElementById('root')!).render(
<OnRampProvider apiKey="onr_xxxxxxxxxxxx" appVersion="2.4.1">
<App />
</OnRampProvider>
)
Your API key is on the Settings page of each app in the dashboard.
OnRampProvider props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey | string | Yes | - | Your API key from Settings |
appVersion | string | No | - | Version string, e.g. "2.4.1" - shows in the version breakdown |
framework | string | No | 'react' | Label shown in the dashboard - set 'nextjs' to distinguish surfaces |
sessionTimeoutMs | number | No | 1_800_000 | Idle time (ms) before a new session starts (default 30 min) |
autoTrackScrollDepth | boolean | No | true | Record page depth at 25%, 50%, 75%, and 90% |
host | string | No | https://ingest.getonramp.dev | Override ingestion endpoint (for self-hosting) |
Scroll depth
The provider automatically records real scrolls at 25%, 50%, 75%, and 90% of
each page. These engagement events appear in page analytics and session
timelines, but never become funnel milestones. Pass
autoTrackScrollDepth={false} to disable collection.
Tracking steps
useTrackStep(name, options?)
Fires a funnel milestone the moment the component mounts. Re-fires if name changes.
'use client'
import { useTrackStep } from '@onramp-sdk/react'
function ProfileSetup() {
useTrackStep('profile_setup_viewed')
return <form>...</form>
}
Options
| Option | Type | Description |
|---|---|---|
properties | Record<string, string | number | boolean> | Custom key-value data attached to the event |
enabled | boolean | Skip tracking while false - useful for gating on a loaded/ready state (default true) |
// Only track once data has loaded
useTrackStep('checkout_viewed', {
enabled: !!user,
properties: { plan: user?.plan },
})
useOnRamp()
Returns the tracker API from any client component below <OnRampProvider>.
'use client'
import { useOnRamp } from '@onramp-sdk/react'
function UpgradeButton() {
const { step, getIds } = useOnRamp()
async function handleClick() {
step('upgrade_clicked', { properties: { plan: 'pro' } })
// Use your authenticated account ID for durable server-side correlation.
const { anonymousId, sessionId } = getIds()
await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({ anonymousId, sessionId, plan: 'pro' }),
})
}
return <button onClick={handleClick}>Upgrade</button>
}
API
| Method | Description |
|---|---|
step(name, options?) | Record a funnel milestone |
identify(traits) | Associate the current user with known traits for integration matching (optional) |
flush() | Flush queued events immediately (also runs on tab close) |
newSession() | Force-start a new session - call after logout |
getIds() | Returns per-page client placeholders; do not persist them in anonymous web mode |
identify(traits)
Associates the current anonymous user with known identity traits. Call once after sign-in so integrations (Stripe, RevenueCat) can match the user to external records.
'use client'
import { useOnRamp } from '@onramp-sdk/react'
function AuthCallback({ user }: { user: User }) {
const { identify } = useOnRamp()
useEffect(() => {
identify({ email: user.email, userId: user.id })
}, [user.id])
}
identify() is entirely optional. Omit it if you have no integrations connected, or if users in your app prefer not to share identity traits. All funnel and retention features work without it.
Route tracking (Next.js App Router)
OnRampRouteTracker auto-records every App Router navigation as a nav_entered event. These appear in session timelines and the journey map but are kept out of your defined funnels automatically.
Mount it once inside OnRampProvider in your root layout:
// app/layout.tsx
import { OnRampProvider } from '@onramp-sdk/react'
import { OnRampRouteTracker } from '@onramp-sdk/react/next'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<OnRampProvider apiKey="onr_xxxxxxxxxxxx" framework="nextjs">
<OnRampRouteTracker />
{children}
</OnRampProvider>
</body>
</html>
)
}
OnRampRouteTracker is exported from @onramp-sdk/react/next (a separate entry point) so that non-Next.js React apps don't pull in the next package.
Crawler tracking (Next.js middleware)
OnRampProvider and OnRampRouteTracker only see traffic that runs your page's JavaScript. AI and search crawlers - GPTBot, ClaudeBot, PerplexityBot, Googlebot, and similar - fetch raw HTML and never execute it, so they never appear in your funnels or session data no matter how much they crawl your site.
withOnRampCrawlerTracking reports these visits from Next.js middleware instead, where the request is seen before any JS runs:
// middleware.ts
import { withOnRampCrawlerTracking } from '@onramp-sdk/react/next'
export default withOnRampCrawlerTracking({ apiKey: 'onr_xxxxxxxxxxxx' })
export const config = {
matcher: '/((?!_next/static|_next/image|favicon.ico).*)',
}
Already have middleware (auth, redirects, etc.)? Pass it as the second argument to compose rather than replace it - your middleware still runs on every request unchanged:
// middleware.ts
import { withOnRampCrawlerTracking } from '@onramp-sdk/react/next'
import { yourExistingMiddleware } from './your-middleware'
export default withOnRampCrawlerTracking({ apiKey: 'onr_xxxxxxxxxxxx' }, yourExistingMiddleware)
export const config = yourExistingMiddleware.config
It only makes a request when the incoming User-Agent matches a known crawler - human traffic is untouched and still tracked by the client SDK as usual. The report itself fires via event.waitUntil, so it never delays the response, and forwards the crawler's real User-Agent and IP so it's classified and geolocated the same way any other crawler visit is.
Next.js middleware only. There's no framework-agnostic hook for "before any JS runs" - non-Next.js servers can call reportCrawlerVisit and isKnownCrawler from @onramp-sdk/core directly from their own request-handling layer (Express middleware, an edge function, etc.).
Session management
Call newSession() after logout so the next user gets a clean session:
'use client'
import { useOnRamp } from '@onramp-sdk/react'
function LogoutButton() {
const { newSession } = useOnRamp()
async function handleLogout() {
await signOut()
newSession()
}
return <button onClick={handleLogout}>Sign out</button>
}
Storage
The React SDK writes no analytics ID or session state to cookies, localStorage, or sessionStorage. The ingestion service derives a daily pseudonymous ID from the request and keeps a server-side session for up to 30 minutes. Calling identify() sends personal traits and is the app developer's responsibility to disclose and govern.
TypeScript
The SDK ships full TypeScript types. No @types/ package needed.
import type { OnRampApi } from '@onramp-sdk/react'
function MyComponent() {
const onramp: OnRampApi = useOnRamp()
// ...
}
