Install the Package
npm install @grainql/analytics-web
yarn add @grainql/analytics-web
pnpm add @grainql/analytics-web
Wrap Your App
Add the GrainProvider at the top of your component tree:
import { GrainProvider } from '@grainql/analytics-web/react';
function App() {
return (
<GrainProvider config={{ tenantId: 'your-tenant-id' }}>
<YourApp />
</GrainProvider>
);
}
Replace 'your-tenant-id' with the alias from your dashboard (not the UUID).
Pro tip: Store your tenant ID in an environment variable like VITE_GRAIN_TENANT_ID or REACT_APP_GRAIN_TENANT_ID.
Track Events
Use the useTrack hook anywhere in your app:
import { useTrack } from '@grainql/analytics-web/react';
function SignupButton() {
const track = useTrack();
const handleClick = () => {
track('signup_clicked', {
location: 'hero',
button_text: 'Get Started'
});
// Your signup logic here
};
return <button onClick={handleClick}>Get Started</button>;
}
Events are automatically batched and sent every few seconds. No manual flushing needed.
Use Remote Config
Access configuration values with useConfig:
import { useConfig } from '@grainql/analytics-web/react';
function Hero() {
const { value: heroText, loading } = useConfig('hero_text');
return (
<div>
<h1>{heroText || 'Welcome!'}</h1>
{loading && <span>Loading fresh content...</span>}
</div>
);
}
The component automatically re-renders when config updates. Default values load instantly from cache or the defaultConfigurations you provided.
How it works: Config uses a cache-first strategy. Your UI loads instantly with cached values, while fresh data loads in the background.
Set User Identity
Identify users to track them across sessions:
import { useGrainAnalytics } from '@grainql/analytics-web/react';
function LoginForm() {
const { identify } = useGrainAnalytics();
const handleLogin = async (email, password) => {
const user = await yourLoginFunction(email, password);
// Associate all future events with this user
identify(user.id, {
email: user.email,
name: user.name,
plan: user.plan
});
};
return <form onSubmit={handleLogin}>...</form>;
}
Complete Example
Here’s a small app that puts it all together:
import {
GrainProvider,
useTrack,
useConfig,
useGrainAnalytics
} from '@grainql/analytics-web/react';
// 1. Wrap your app
function App() {
return (
<GrainProvider
config={{
tenantId: 'your-tenant-id',
defaultConfigurations: {
hero_text: 'Welcome to Our App',
feature_enabled: 'false'
}
}}
>
<HomePage />
</GrainProvider>
);
}
// 2. Use hooks in components
function HomePage() {
const track = useTrack();
const { value: heroText } = useConfig('hero_text');
const { value: featureEnabled } = useConfig('feature_enabled');
const { identify } = useGrainAnalytics();
const handleSignup = (userId: string) => {
// Identify the user
identify(userId, { signup_date: new Date().toISOString() });
// Track the event
track('signup_completed', {
method: 'email'
});
};
return (
<div>
<h1>{heroText}</h1>
{featureEnabled === 'true' && (
<NewFeature />
)}
<button onClick={() => track('cta_clicked', { location: 'hero' })}>
Get Started
</button>
</div>
);
}
Track Page Views
For single-page apps, track route changes manually:
import { useEffect } from 'react';
import { useTrack } from '@grainql/analytics-web/react';
import { useLocation } from 'react-router-dom'; // or your router
function PageViewTracker() {
const track = useTrack();
const location = useLocation();
useEffect(() => {
track('page_viewed', {
page: location.pathname,
title: document.title
});
}, [location.pathname, track]);
return null; // This component just tracks
}
// Add to your app
function App() {
return (
<GrainProvider config={{ tenantId: 'your-tenant-id' }}>
<Router>
<PageViewTracker />
<Routes>...</Routes>
</Router>
</GrainProvider>
);
}
What’s Next?
React Hooks Reference
See all available hooks and their options
User Identification
Learn about tracking users across sessions
Authentication
Secure your analytics with Auth0 or JWT
Event Best Practices
Learn what to track and how to structure events
TypeScript users: All hooks are fully typed. You’ll get autocomplete and type checking out of the box.