To use FullStory with V0 by Vercel, add the FullStory snippet to your Next.js app using the next/script component, then create a Next.js API route at app/api/fullstory/route.ts to fetch session replay data server-side. Store your FullStory org ID client-side with NEXT_PUBLIC_ prefix and your API key server-side without the prefix.
Adding FullStory Session Replay to Your V0-Generated Next.js App
V0 by Vercel is excellent for scaffolding polished UIs quickly, but understanding how users actually interact with those interfaces requires session replay tooling. FullStory records every click, scroll, rage click, and navigation event and lets you replay entire user sessions — making it invaluable when debugging UX issues that never surface in error logs.
Integrating FullStory into a V0-generated Next.js app has two distinct layers. The first is the client-side recording snippet, which FullStory provides as a JavaScript tag. In Next.js, you load third-party scripts through the next/script component rather than a raw script tag — this gives the Next.js runtime control over when the script loads and avoids blocking the critical render path. The snippet goes in your root layout so it fires on every page.
The second layer is optional but powerful: the FullStory Data Export API lets you pull session metadata, events, and user segments into your own dashboards. This requires a server-side API route so your FullStory API key never appears in client JavaScript. Once connected, you can build internal analytics views directly in your V0 app — showing which pages have the most rage clicks, which user segments drop off, and which sessions to replay for debugging. V0 generates the dashboard UI components, and your API route handles the data fetching.
Integration method
V0 generates your app UI and you inject the FullStory recording snippet via Next.js Script component in the root layout. A separate API route handles server-side calls to the FullStory Data Export API for fetching session data and events. The org ID is exposed client-side with NEXT_PUBLIC_ prefix while the API key stays server-only.
Prerequisites
- A V0 account at v0.dev — free tier works for this tutorial
- A FullStory account with your Org ID — found in Settings → General → Organization
- A FullStory API key for the Data Export API — generated in Settings → Integrations → API Keys
- A Vercel account (free) for deploying your app and setting environment variables
- Basic familiarity with editing files in V0's Code panel
Step-by-step guide
Generate Your App UI in V0
Generate Your App UI in V0
Open V0 at v0.dev and create or open the project you want to track with FullStory. Use V0's chat panel to generate the pages and components you want to monitor — landing pages, onboarding flows, checkout steps, or any user-facing interface. Be specific in your prompts: describe the layout, the interactive elements (buttons, forms, modals), and the user journey you expect visitors to follow. V0 generates fully functional React components with Tailwind CSS styling by default. If you already have an existing V0 project, you can skip UI generation and jump straight to Step 2. The FullStory snippet works with any Next.js app structure — it doesn't require changes to individual components, only to the root layout. Take note of the pages in your app that are most important to track: high-traffic landing pages, sign-up forms, checkout flows, and any page where users have reported confusion. FullStory records everything automatically once the snippet is loaded, but having a clear list of critical pages helps when you review sessions later. Tip: V0's Design Mode (Option+D) lets you adjust colors and spacing without spending credits — polish the visual design before wiring up analytics so you're tracking the final version of your UI.
Create a three-page Next.js app with a landing page, a pricing page with three tiers, and a contact form page. Use a shared navigation header with links between pages. Each page should have a hero section and body content sections. Use a clean, modern design with a blue and white color scheme.
Paste this in V0 chat
Pro tip: FullStory tracks all pages automatically once the snippet is in the root layout — no need to add tracking code to individual components.
Expected result: A multi-page Next.js app renders in the V0 preview with navigation between pages. The UI is polished and ready for analytics instrumentation.
Add the FullStory Script to the Root Layout
Add the FullStory Script to the Root Layout
The FullStory recording snippet must load on every page of your app. In Next.js App Router, the correct place for global scripts is the root layout file at app/layout.tsx. Open this file in V0's Code panel and add the FullStory snippet using the next/script component — never use a raw HTML script tag, as this can conflict with React's rendering lifecycle and cause hydration mismatches. The snippet uses your FullStory Org ID, which is safe to expose client-side because it only identifies which FullStory account to send data to — it does not grant API access. Prefix it with NEXT_PUBLIC_ so Next.js inlines it into the client bundle. Your FullStory Org ID is found in FullStory → Settings → General → Organization ID. The next/script component accepts a strategy prop that controls when the script loads. Use strategy='afterInteractive' for FullStory — this loads the script after the page becomes interactive, avoiding any impact on First Contentful Paint (FCP) or Largest Contentful Paint (LCP) scores. The 'beforeInteractive' strategy would block rendering, and 'lazyOnload' might miss early user interactions. After adding the snippet to the layout, verify in V0's preview that the console shows no script loading errors. You won't see FullStory data in the dashboard until you deploy to Vercel with the real org ID, since the V0 preview runs on a sandbox domain that may not match your FullStory allowed origins.
Update app/layout.tsx to import Script from 'next/script' and add the FullStory initialization script using strategy='afterInteractive'. The script should set window._fs_debug = false and window._fs_host = 'fullstory.com', then load the FullStory snippet. Use process.env.NEXT_PUBLIC_FULLSTORY_ORG_ID as the org ID.
Paste this in V0 chat
1import Script from 'next/script';23export default function RootLayout({4 children,5}: {6 children: React.ReactNode;7}) {8 return (9 <html lang="en">10 <body>11 {children}12 <Script13 id="fullstory-init"14 strategy="afterInteractive"15 dangerouslySetInnerHTML={{16 __html: `17 window['_fs_debug'] = false;18 window['_fs_host'] = 'fullstory.com';19 window['_fs_script'] = 'edge.fullstory.com/s/fs.js';20 window['_fs_org'] = '${process.env.NEXT_PUBLIC_FULLSTORY_ORG_ID}';21 window['_fs_namespace'] = 'FS';22 (function(m,n,e,t,l,o,g,y){23 if (e in m) { if(m.console && m.console.log) { m.console.log('FullStory namespace conflict. Please set window[\'_fs_namespace\'].'); } return;}24 g=m[e]=function(a,b,s){g.q?g.q.push([a,b,s]):g._api(a,b,s);};25 g.q=[];26 o=n.createElement(t);27 o.async=1;28 o.crossOrigin='anonymous';29 o.src='https://'+window['_fs_script'];30 y=n.getElementsByTagName(t)[0];31 y.parentNode.insertBefore(o,y);32 })(window,document,window['_fs_namespace'],'script');33 `,34 }}35 />36 </body>37 </html>38 );39}Pro tip: Use strategy='afterInteractive' — not 'beforeInteractive' — so the FullStory script does not block your page's initial render and hurt Core Web Vitals scores.
Expected result: The root layout includes the FullStory initialization script. The app builds without errors. The script loads after page interactivity in the browser network tab.
Create the FullStory API Route for Data Fetching
Create the FullStory API Route for Data Fetching
The FullStory Data Export API lets you fetch session metadata, user events, and behavioral analytics programmatically. Because this API requires your FullStory API key — which must never appear in client-side JavaScript — all calls go through a Next.js API route at app/api/fullstory/route.ts. This route handler accepts GET requests with query parameters (such as a date range or user ID) and proxies the request to FullStory's REST API using your server-side API key stored in process.env.FULLSTORY_API_KEY. It returns the response data as JSON to your frontend components. The FullStory Data Export API base URL is https://export.fullstory.com/api/v1/. Common endpoints include /export/list (list available data exports), /users/v1/individual (get data for a specific user), and /sessions/v1/individual (get sessions for a user). For newer FullStory accounts using the FullStory v2 REST API, the base is https://api.fullstory.com/v2/ with endpoints like /sessions and /events. Structure the route to handle different FullStory endpoints through a single proxy — use a type query parameter to route requests to the appropriate FullStory API endpoint. Always validate incoming query parameters and never forward arbitrary URLs to prevent server-side request forgery (SSRF). Add proper error handling that returns meaningful error messages when the FullStory API responds with 4xx or 5xx codes.
Create a Next.js API route at app/api/fullstory/sessions/route.ts that fetches a list of recent user sessions from the FullStory API. The route should accept optional query parameters for userId and dateRange, then forward an authenticated GET request to the FullStory v2 API at https://api.fullstory.com/v2/sessions. Return the sessions array as JSON. Use process.env.FULLSTORY_API_KEY as the Bearer token.
Paste this in V0 chat
1import { NextRequest, NextResponse } from 'next/server';23const FULLSTORY_API_BASE = 'https://api.fullstory.com/v2';45export async function GET(request: NextRequest) {6 const apiKey = process.env.FULLSTORY_API_KEY;78 if (!apiKey) {9 return NextResponse.json(10 { error: 'FullStory API key not configured' },11 { status: 500 }12 );13 }1415 const { searchParams } = new URL(request.url);16 const userId = searchParams.get('userId');17 const limit = searchParams.get('limit') || '20';1819 const params = new URLSearchParams({ limit });20 if (userId) params.set('uid', userId);2122 try {23 const response = await fetch(24 `${FULLSTORY_API_BASE}/sessions?${params.toString()}`,25 {26 headers: {27 Authorization: `Basic ${Buffer.from(`${apiKey}:`).toString('base64')}`,28 'Content-Type': 'application/json',29 },30 next: { revalidate: 60 },31 }32 );3334 if (!response.ok) {35 const errorText = await response.text();36 return NextResponse.json(37 { error: `FullStory API error: ${response.status}`, details: errorText },38 { status: response.status }39 );40 }4142 const data = await response.json();43 return NextResponse.json(data);44 } catch (error) {45 return NextResponse.json(46 { error: 'Failed to fetch FullStory sessions' },47 { status: 500 }48 );49 }50}Pro tip: Add next: { revalidate: 60 } to your fetch calls inside API routes to cache FullStory responses for 60 seconds, reducing API calls and staying within FullStory's rate limits.
Expected result: The API route at /api/fullstory/sessions responds to GET requests. In Vercel logs, the route appears as a serverless function. Calling it with a valid FULLSTORY_API_KEY returns session data from FullStory.
Set Environment Variables in Vercel
Set Environment Variables in Vercel
FullStory requires two different types of credentials stored in two different ways. Your Org ID (the short alphanumeric code like 'ABC123') is used by the client-side snippet and must be prefixed with NEXT_PUBLIC_ so Next.js includes it in the browser bundle. Your API key is used server-side only and must never have the NEXT_PUBLIC_ prefix — it stays locked in Vercel's server environment. To configure these, push your V0 project to GitHub using V0's Git panel (Git panel → Connect to GitHub → push). Then open your Vercel project at vercel.com/dashboard, navigate to your project, and go to Settings → Environment Variables. Add the first variable: key = NEXT_PUBLIC_FULLSTORY_ORG_ID, value = your FullStory Org ID (found in FullStory → Settings → General → Organization). Check all three environment checkboxes: Production, Preview, and Development. Click Save. Add the second variable: key = FULLSTORY_API_KEY, value = your FullStory API key (generated in FullStory → Settings → Integrations → API Keys). Check all three environment checkboxes. Click Save. After saving both variables, trigger a new deployment by going to Deployments and clicking Redeploy — environment variable changes require a fresh deployment to take effect. For local development, create a .env.local file at the root of your project with both variables and add this file to .gitignore immediately to prevent accidentally committing credentials to GitHub.
1# .env.local — never commit this file to git2NEXT_PUBLIC_FULLSTORY_ORG_ID=your_org_id_here3FULLSTORY_API_KEY=your_api_key_herePro tip: The NEXT_PUBLIC_FULLSTORY_ORG_ID is baked into the JavaScript bundle at build time — if you change it, you must redeploy even if you update the Vercel Dashboard variable.
Expected result: Both environment variables appear in Vercel Dashboard under Settings → Environment Variables. After redeployment, the FullStory snippet loads with your real Org ID in the browser's network tab.
Add User Identification and Build Your Replay Dashboard
Add User Identification and Build Your Replay Dashboard
FullStory automatically records sessions for all visitors, but sessions are much more useful when linked to real user identities. The FS.identify() call lets you attach a user ID, email, and custom properties to the current session so your team can search for 'all sessions by user@company.com' and replay exactly what they experienced. Call FS.identify() after the user successfully authenticates. If you're using Clerk, Auth.js, or Supabase Auth in your V0 app, this call should go in a client component that runs once after login. The function signature is FS.identify(userId: string, userVars: object) — pass the user's unique ID as the first argument and an object with email, displayName, and any custom properties as the second. Next, build the session replay dashboard using V0's UI generation. Prompt V0 to generate a dashboard page that fetches sessions from your /api/fullstory/sessions route and displays them in a table. Include columns for session ID, user email, page visited, session duration, and a link to the FullStory replay URL. The replay URL format is https://app.fullstory.com/ui/{orgId}/session/{sessionId}. For complex analytics dashboards or integrations that pull FullStory data into existing CRM or data warehouse pipelines, RapidDev's team can help design the architecture and set up scheduled exports. The combination of V0-generated UI and FullStory data creates a powerful internal analytics tool without building a separate data pipeline.
Create a 'Session Replays' admin page at app/admin/sessions/page.tsx. Add a data table with columns: User Email, Session Start, Duration (minutes), Pages Visited, and a 'View Replay' link button. Fetch session data from GET /api/fullstory/sessions on page load. Add a search input to filter by email and a date range picker. Show a loading skeleton while data loads and an empty state if no sessions are returned.
Paste this in V0 chat
1'use client';23import { useEffect } from 'react';45interface UserData {6 id: string;7 email: string;8 displayName: string;9 plan?: string;10}1112export function FullStoryIdentify({ user }: { user: UserData }) {13 useEffect(() => {14 if (typeof window !== 'undefined' && window.FS && user.id) {15 window.FS.identify(user.id, {16 email: user.email,17 displayName: user.displayName,18 plan_str: user.plan ?? 'free',19 });20 }21 }, [user.id, user.email, user.displayName, user.plan]);2223 return null;24}2526// Add TypeScript declaration for FullStory global27declare global {28 interface Window {29 FS: {30 identify: (uid: string, userVars: Record<string, unknown>) => void;31 event: (eventName: string, eventProperties: Record<string, unknown>) => void;32 setUserVars: (userVars: Record<string, unknown>) => void;33 };34 }35}Pro tip: Add the TypeScript global declaration for window.FS to avoid TypeScript errors when calling FullStory methods — FullStory does not ship official type definitions by default.
Expected result: After login, the browser console shows no errors from the FS.identify() call. In the FullStory dashboard, sessions are now labeled with user emails instead of 'Anonymous'. The session table on the admin page displays real session data fetched from the API route.
Deploy to Vercel and Verify Recording
Deploy to Vercel and Verify Recording
With the FullStory snippet in the layout, the API route created, and environment variables configured, deploy your app to Vercel. If your V0 project is connected to GitHub, a push automatically triggers deployment. Otherwise, use V0's built-in Publish flow or the Vercel Dashboard → Deployments → Redeploy. Wait for the deployment to complete (usually 30–60 seconds). Open the production URL and navigate through a few pages. After a minute or two, log in to your FullStory account at app.fullstory.com. Go to Sessions → Recent Sessions — you should see your own session appear with your production domain. Click on the session to watch the replay and confirm that all interactions are being captured correctly. If you don't see sessions appearing, check the browser's developer console (F12 → Console) for FullStory-related errors. Common issues include the org ID being wrong or missing (check NEXT_PUBLIC_FULLSTORY_ORG_ID in Vercel Dashboard), the script being blocked by an ad blocker (test in a private browser window without extensions), or the domain not being in FullStory's allowed origins (FullStory → Settings → General → Allowed Domains). Verify the API route is working by navigating to your-app.vercel.app/api/fullstory/sessions in the browser — if configured correctly, it returns a JSON response with session data rather than an error. Check the Vercel Dashboard → Functions tab to see the API route listed and review invocation logs for any authentication errors.
Pro tip: Test in a browser without extensions — ad blockers and privacy tools frequently block FullStory's recording script, which can make it look like the integration isn't working when it's actually fine.
Expected result: Sessions from your production domain appear in the FullStory dashboard within 2 minutes of page visits. The /api/fullstory/sessions route returns JSON data. Vercel deployment logs show no errors.
Common use cases
UX Debugging Dashboard
Build an internal dashboard that surfaces FullStory sessions where users encountered errors or rage-clicked. Filter sessions by page, user segment, or event type and link directly to FullStory replays for investigation.
Create an internal analytics dashboard with a session list table showing columns for user ID, page visited, session duration, and rage click count. Add a 'View Replay' button in each row that links to a FullStory session URL. Include a filter bar with dropdowns for date range and page path. The table should fetch data from GET /api/fullstory/sessions.
Copy this prompt to try it in V0
User Identification on Signup
Automatically tag FullStory sessions with user identity data (name, email, account tier) when a user signs in. This links anonymous recordings to real users so your team can replay sessions for specific customers during support calls.
Build a post-login page component that calls FS.identify() with the user's ID, email, and display name after successful authentication. Show a brief 'Welcome back' greeting while the identify call fires. The component should receive user data as props from a server component that reads the session.
Copy this prompt to try it in V0
Heatmap Insights Widget
Embed a compact widget in your admin panel that shows the top clicked elements on a given page based on FullStory click data fetched via the Data Export API. Product teams can check interaction patterns without leaving the app.
Create a 'Page Insights' widget card with a URL input at the top, a submit button, and a ranked list of the most clicked elements on that page. Each row should show the CSS selector, click count, and a percentage bar. Fetch the data from POST /api/fullstory/clicks with the page URL in the request body.
Copy this prompt to try it in V0
Troubleshooting
No sessions appear in the FullStory dashboard after deploying
Cause: The NEXT_PUBLIC_FULLSTORY_ORG_ID variable is missing, incorrect, or was added after the last deploy without triggering a redeployment. Since NEXT_PUBLIC_ variables are inlined at build time, a post-deploy variable change requires rebuilding.
Solution: Go to Vercel Dashboard → your project → Settings → Environment Variables. Confirm NEXT_PUBLIC_FULLSTORY_ORG_ID is present and matches the Org ID in FullStory → Settings → General. Then go to Deployments and click Redeploy on the latest deployment to rebuild with the updated variable.
FullStory script loads but records nothing on localhost — only works on the deployed site
Cause: FullStory restricts recording to domains listed in your account's allowed origins. The localhost domain is not on the list by default.
Solution: In FullStory, go to Settings → General → Allowed Domains and add 'localhost' to the list. Alternatively, use a tool like ngrok to expose your local dev server on a public domain that FullStory already allows. For development testing, always check the FullStory allowed domains list first.
API route returns 401 Unauthorized when fetching FullStory sessions
Cause: The FULLSTORY_API_KEY environment variable is not set in Vercel, is set with the wrong value, or was added after the last deployment without redeploying.
Solution: Go to Vercel Dashboard → Settings → Environment Variables and verify FULLSTORY_API_KEY is set correctly. Make sure you are using a FullStory API key (from Settings → Integrations → API Keys), not the Org ID. Redeploy after confirming the variable is correct.
1// Verify the Authorization header format — FullStory uses Basic auth with the API key as the username2Authorization: `Basic ${Buffer.from(`${apiKey}:`).toString('base64')}`window.FS is undefined when calling FS.identify() after login
Cause: The FullStory script uses strategy='afterInteractive' and may not have finished loading when the identify call fires, especially if login happens quickly on page load.
Solution: Wrap the FS.identify() call in a check for window.FS existence and use a small polling mechanism or listen for the FullStory 'ready' state before calling identify.
1useEffect(() => {2 const tryIdentify = () => {3 if (typeof window !== 'undefined' && window.FS) {4 window.FS.identify(user.id, { email: user.email, displayName: user.displayName });5 } else {6 setTimeout(tryIdentify, 300);7 }8 };9 tryIdentify();10}, [user.id]);Best practices
- Always load the FullStory snippet with next/script strategy='afterInteractive' — never with a raw script tag or 'beforeInteractive' which blocks rendering
- Call FS.identify() immediately after authentication so every session is linked to a real user, making debugging and support dramatically easier
- Store the Org ID as NEXT_PUBLIC_FULLSTORY_ORG_ID (client-safe) and the API key as FULLSTORY_API_KEY (server-only) — never mix these up
- Add your production domain to FullStory's allowed origins before deploying to avoid a confusing 'no sessions' debugging session
- Cache API route responses with next: { revalidate: 60 } to avoid hitting FullStory's rate limits when building dashboards that poll frequently
- Add the TypeScript window.FS type declaration in a global.d.ts file so all FS method calls are type-safe across your codebase
- Use FS.event() to track custom business events (like 'Upgrade Button Clicked' or 'Checkout Started') in addition to automatic interaction recording
- Exclude sensitive pages from recording (like payment forms or SSN inputs) using FullStory's Privacy by Default settings or the data-exclude attribute on form fields
Alternatives
Amplitude is a better choice if you primarily want product analytics with funnel analysis and retention charts rather than visual session replays and heatmaps.
Mixpanel focuses on event tracking and user journey analysis with powerful cohort tools — choose it when you need deep behavioral segmentation rather than visual session recordings.
Segment is a customer data platform that can route events to FullStory and dozens of other tools — use it when you want a single tracking implementation that feeds multiple analytics destinations.
Google Analytics is free and covers traffic and conversion analytics well — choose it for SEO and acquisition metrics, but add FullStory when you need to understand what users do after landing on your site.
Frequently asked questions
Does FullStory work with V0's preview sandbox?
FullStory typically does not record sessions in V0's preview sandbox because the preview URL (*.v0.dev) may not be in your FullStory allowed origins list. Add the preview domain to FullStory → Settings → General → Allowed Domains if you want to test recording before deploying. For most teams, it's easier to test on the Vercel production or preview deployment URL instead.
Is the FullStory Org ID safe to expose publicly in client-side code?
Yes. The Org ID only tells the FullStory script which account to send data to — it cannot be used to access your FullStory data or make API calls. Keep the NEXT_PUBLIC_ prefix to expose it client-side. Your FullStory API key, on the other hand, must stay server-side without the NEXT_PUBLIC_ prefix since it grants read access to your session data.
Will FullStory slow down my V0-generated Next.js app?
With strategy='afterInteractive', the FullStory script loads after the page is interactive and does not affect your First Contentful Paint or Largest Contentful Paint scores. The script itself is ~50KB and loads asynchronously. For high-performance apps, you can further defer it with strategy='lazyOnload', though this risks missing some early-page interactions.
How do I prevent FullStory from recording sensitive form fields?
Add the data-hj-suppress attribute (FullStory respects standard privacy attributes) or use the fs-exclude CSS class on sensitive input fields. For entire pages containing sensitive data (like credit card forms), you can use FS.consent(false) to pause recording. FullStory also masks all text inputs by default in Privacy Mode — check Settings → Privacy to configure your masking level.
Can I use FullStory with Vercel's Edge Functions?
The FullStory client-side snippet works regardless of whether your server uses Edge Functions or Node.js — it's a browser-side library. However, the FullStory Data Export API calls in your Next.js API routes use Node.js APIs like Buffer.from() for base64 encoding, which are not available in Edge Functions. Keep the FullStory API route as a standard Node.js serverless function without adding export const runtime = 'edge'.
How do I track custom events in FullStory?
Use window.FS.event('EventName', { property: value }) to log custom events — for example, FS.event('Upgrade Clicked', { plan: 'pro', source: 'pricing-page' }). These events appear in session replays at the exact moment they fired and can be used to create funnels in FullStory's analytics. Call FS.event() from client components after the FullStory script has loaded.
What is the difference between FullStory's Org ID and API key?
The Org ID is a public identifier for your FullStory account used by the client-side recording snippet — it's safe to include in browser JavaScript and should use the NEXT_PUBLIC_ prefix in Next.js. The API key is a server-side credential that grants programmatic access to the FullStory Data Export API — it must stay in server-only environment variables and should never appear in client-side code.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation