React SDK
First-class React integration with hooks, components, and HOCs. Built on top of the JavaScript SDK.
Installation
npm install @retenshun/react # or yarn add @retenshun/react
The React SDK includes the JavaScript SDK as a dependency — no need to install both.
Provider Setup
Wrap your app with RetenshunProvider at the root level:
// app/layout.tsx (Next.js App Router)
import { RetenshunProvider } from '@retenshun/react'
export default function RootLayout({ children }) {
return (
<html>
<body>
<RetenshunProvider
projectId="YOUR_PROJECT_ID"
apiKey="pk_live_YOUR_PUBLIC_KEY"
config={{
apiUrl: 'https://your-domain.com', // optional
debug: false, // optional
autoTrack: { pageViews: true }, // optional
}}
>
{children}
</RetenshunProvider>
</body>
</html>
)
}Pages Router
// pages/_app.tsx
import { RetenshunProvider } from '@retenshun/react'
export default function App({ Component, pageProps }) {
return (
<RetenshunProvider
projectId="YOUR_PROJECT_ID"
apiKey="pk_live_YOUR_PUBLIC_KEY"
>
<Component {...pageProps} />
</RetenshunProvider>
)
}useRetenshun Hook
The main hook that gives you access to everything:
import { useRetenshun } from '@retenshun/react'
function Dashboard() {
const {
isReady, // boolean — SDK initialized
identify, // (userId, properties?) => void
track, // (eventName, properties?) => void
page, // (pageName?, properties?) => void
setUserProperties, // (properties) => void
reset, // () => void
getUserId, // () => string | null
ecommerce, // e-commerce helpers
} = useRetenshun()
// Wait for SDK
if (!isReady) return null
return <div>Dashboard for {getUserId()}</div>
}Identify Users
import { useRetenshun } from '@retenshun/react'
function LoginForm() {
const { identify } = useRetenshun()
async function handleLogin(email, password) {
const user = await api.login(email, password)
// Identify after successful login
identify(user.id, {
email: user.email,
name: user.name,
plan: user.subscription.plan,
})
}
return <form onSubmit={...}>...</form>
}Track Events
import { useTrack } from '@retenshun/react'
function UpgradeButton() {
const { track } = useTrack()
return (
<button
onClick={() => {
track('upgrade_clicked', { from: 'free', to: 'pro' })
// ... handle upgrade
}}
>
Upgrade to Pro
</button>
)
}Specialized Hooks
Use focused hooks when you only need one capability:
import { useTrack, useIdentify, useEcommerce } from '@retenshun/react'
// Only tracking
function FeatureComponent() {
const { track, isReady } = useTrack()
// ...
}
// Only identification
function AuthWrapper() {
const { identify, isReady } = useIdentify()
// ...
}
// Only e-commerce
function ProductPage({ product }) {
const { ecommerce, isReady } = useEcommerce()
useEffect(() => {
if (isReady) {
ecommerce.productViewed({
product_id: product.id,
name: product.name,
price: product.price,
})
}
}, [isReady, product.id])
return <div>...</div>
}Tracking Components
Declarative components for common tracking patterns:
TrackPageView
import { TrackPageView } from '@retenshun/react'
// Tracks a page view when this component mounts
function PricingPage() {
return (
<>
<TrackPageView name="Pricing" properties={{ variant: 'annual' }} />
<h1>Pricing</h1>
{/* ... */}
</>
)
}TrackEvent
import { TrackEvent } from '@retenshun/react'
// Tracks an event when this component mounts
function OnboardingComplete() {
return (
<>
<TrackEvent
event="onboarding_completed"
properties={{ steps: 5 }}
once={true} // Only track once per session
/>
<h1>You're all set!</h1>
</>
)
}Higher-Order Component
Automatically track when a component is viewed:
import { withTracking } from '@retenshun/react'
function FeatureCard({ name, description }) {
return (
<div>
<h3>{name}</h3>
<p>{description}</p>
</div>
)
}
// Tracks "feature_card_viewed" every time this component mounts
export default withTracking(
FeatureCard,
'feature_card_viewed',
(props) => ({ feature: props.name }) // optional: extract properties from props
)E-commerce
import { useEcommerce } from '@retenshun/react'
function ProductPage({ product }) {
const { ecommerce } = useEcommerce()
return (
<div>
<h1>{product.name}</h1>
<button
onClick={() =>
ecommerce.addedToCart({
product_id: product.id,
name: product.name,
price: product.price,
quantity: 1,
})
}
>
Add to Cart
</button>
</div>
)
}
// Checkout flow
function CheckoutPage({ cart }) {
const { ecommerce } = useEcommerce()
useEffect(() => {
ecommerce.checkoutStarted({
cart_id: cart.id,
total: cart.total,
item_count: cart.items.length,
})
}, [])
async function handlePurchase() {
const order = await api.placeOrder(cart)
ecommerce.orderCompleted({
order_id: order.id,
total: order.total,
currency: 'USD',
products: order.items,
})
}
return <button onClick={handlePurchase}>Place Order</button>
}Logout / Reset
import { useRetenshun } from '@retenshun/react'
function UserMenu() {
const { reset } = useRetenshun()
return (
<button onClick={() => {
reset() // Clear Retenshun identity
auth.signOut() // Your auth logout
}}>
Sign Out
</button>
)
}Complete Example
// app/layout.tsx
import { RetenshunProvider } from '@retenshun/react'
export default function RootLayout({ children }) {
return (
<html>
<body>
<RetenshunProvider
projectId="proj_abc123"
apiKey="pk_live_your_key"
>
{children}
</RetenshunProvider>
</body>
</html>
)
}
// app/page.tsx
import { useRetenshun, TrackPageView } from '@retenshun/react'
export default function HomePage() {
const { track, identify, isReady } = useRetenshun()
return (
<>
<TrackPageView name="Home" />
<h1>Welcome</h1>
<button onClick={() => track('cta_clicked')}>
Get Started
</button>
</>
)
}