SDK — React Router
Install `@layers/client` for React Router v6 (BrowserRouter) and v7 (data router + framework mode). Auto-screen via the router hooks.
React Router has two materially different shapes today — v6 (the classic BrowserRouter + Routes + Route JSX tree) and v7 (the data router with loaders + the optional framework mode that enables SSR). The SDK install is the same; the integration touchpoints differ.
Install
npm i @layers/clientv6 — BrowserRouter
Wrap the router with a Layers provider. Use useLocation in a tracker component to fire screen() on every route change.
import { LayersClient } from '@layers/client';
import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom';
import { createContext, useContext, useEffect, useState } from 'react';
const LayersContext = createContext<LayersClient | null>(null);
const useLayers = () => useContext(LayersContext);
function PageTracker() {
const layers = useLayers();
const { pathname } = useLocation();
useEffect(() => {
layers?.screen(pathname).catch(() => {});
}, [layers, pathname]);
return null;
}
function LayersProvider({ children }: { children: React.ReactNode }) {
const [client, setClient] = useState<LayersClient | null>(null);
useEffect(() => {
const c = new LayersClient({
apiKey: 'unused',
appId: import.meta.env.VITE_LAYERS_APP_ID,
environment: import.meta.env.MODE === 'production' ? 'production' : 'development',
});
c.init().then(() => setClient(c));
}, []);
return <LayersContext.Provider value={client}>{children}</LayersContext.Provider>;
}
createRoot(document.getElementById('root')!).render(
<BrowserRouter>
<LayersProvider>
<PageTracker />
<Routes>{/* … */}</Routes>
</LayersProvider>
</BrowserRouter>,
);PageTracker must mount inside the router context (so useLocation resolves) and inside LayersProvider (so it can read the client). Render it once near the root.
v7 — Data router (createBrowserRouter)
The hooks are the same; what changes is where you mount the provider. With createBrowserRouter, your route tree is configuration — wrap the <RouterProvider>:
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
const router = createBrowserRouter([
// … your routes
]);
function App() {
return (
<LayersProvider>
<RouterProvider router={router} />
</LayersProvider>
);
}Page tracking still uses useLocation — render <PageTracker /> from a route element (typically the root layout component).
v7 — Framework mode (SSR)
React Router v7 framework mode runs the route tree on the server. Same SSR rules as Next.js apply:
- Never initialize the SDK on the server. Use a
useEffect-based provider, identical to the Next.js App Router pattern. - For server-side event firing (post-purchase webhooks, etc.) call
POST /v1/events— the partner-API forwarding surface — from your loader/action functions.
import { useEffect, useState } from 'react';
import { Outlet, useLocation } from 'react-router';
import { LayersClient } from '@layers/client';
export default function Root() {
const [client, setClient] = useState<LayersClient | null>(null);
const { pathname } = useLocation();
useEffect(() => {
const c = new LayersClient({
apiKey: 'unused',
appId: import.meta.env.VITE_LAYERS_APP_ID,
environment: import.meta.env.MODE === 'production' ? 'production' : 'development',
});
c.init().then(() => setClient(c));
}, []);
useEffect(() => {
client?.screen(pathname).catch(() => {});
}, [client, pathname]);
return <Outlet />;
}Tracking events
Same LayersClient API as every other JS environment:
const layers = useLayers();
layers?.track('purchase_success', { revenue: 19.99, currency: 'USD' });See Standard events for canonical event names; events outside that list are stored but not relayed to CAPI.
Verifying the install
curl -X POST https://api.layers.com/v1/projects/$PROJECT_ID/sdk-apps/$APP_ID/verify-tracking \
-H "Authorization: Bearer $LAYERS_API_KEY"See verify-tracking.
See also
- Vite guide — most React Router apps build with Vite.
- Next.js guide — for App Router apps that aren't using React Router.
- Standard events
- Server-side forwarding