# How to Integrate Bolt.new with FullStory

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 20 minutes
- Last updated: April 2026

## TL;DR

To add FullStory to your Bolt.new app, paste the FullStory JavaScript snippet into your HTML head or use the `@fullstory/browser` npm package. The script loads on the client side and works in both the Bolt WebContainer preview and after deployment. Get your Org ID from the FullStory dashboard, add it to your snippet, and FullStory automatically captures every click, scroll, and rage click without any manual event instrumentation.

## FullStory Session Replay in Bolt.new: See Every User Interaction

FullStory is a session replay and digital experience analytics platform that automatically records every user session — capturing clicks, scrolls, form interactions, and rage clicks without any manual event tagging. Unlike event-based analytics tools that require you to decide upfront which user actions matter, FullStory captures everything and lets you search, filter, and watch replays retroactively. This retroactive analysis is invaluable when users report bugs or unexpected behaviors: instead of asking 'what did you click?', you can watch exactly what happened.

Integrating FullStory with a Bolt.new app is straightforward because the core integration is a JavaScript snippet that runs in the user's browser. This client-side approach means you can add FullStory to your Bolt project and test it in the WebContainer preview immediately — the snippet sends data directly from the browser to FullStory's servers over HTTPS, bypassing the WebContainer's networking constraints entirely. You do not need to deploy to Netlify or Bolt Cloud just to verify that session capture is working.

FullStory's free trial captures unlimited sessions for 14 days. After that, pricing scales by monthly session count. For most early-stage Bolt apps, FullStory's smallest paid plan (starting around 1,000 sessions/month) is sufficient for understanding real user behavior and improving UX. The platform's DX Data feature uses machine learning to automatically surface user frustration signals — rage clicks, error clicks, thrashing mouse movements — without manual configuration.

## Before you start

- A FullStory account (free trial available at fullstory.com) with an Org ID from the FullStory dashboard
- A Bolt.new project — Vite (React) or Next.js both work for the client-side snippet
- For user identification: an existing authentication system (Supabase Auth, Clerk, or similar)
- For the REST API dashboard: a Next.js project with API routes; a FullStory API key generated in Settings → API Keys

## Step-by-step guide

### 1. Get Your FullStory Org ID and Choose an Installation Method

Before adding FullStory to your Bolt app, you need your Org ID from the FullStory dashboard. Log into app.fullstory.com, click the settings gear in the bottom-left corner, and navigate to Settings → General. Your Org ID appears near the top — it is a string like 'o-1XXXXX-na1' or a shorter alphanumeric code depending on your account region. Copy it.

You have two equivalent options for installing FullStory in a Bolt app. Option 1 is the HTML snippet approach: paste a script tag into your HTML's head section. This is the simplest method and requires no npm installation. Option 2 is the @fullstory/browser npm package: install via npm and initialize in your JavaScript/TypeScript code. The npm package is preferred for Bolt because it works cleanly with TypeScript types, integrates naturally with React component lifecycle, and keeps your HTML template clean.

An important FullStory behavior to understand: FullStory does not capture sessions in localhost by default — it captures sessions on your deployed domain. However, you can test that the snippet loads correctly and doesn't throw errors in the Bolt WebContainer preview. During local/preview development, FullStory will receive the initialization call but may throttle or skip recording depending on your plan settings. To enable localhost/preview recording for development, log into FullStory Settings → Privacy → and ensure your preview URL is allowed. Check the browser console for 'FullStory initialized' messages to confirm the script loaded correctly.

```
// lib/fullstory.ts
import * as FullStory from '@fullstory/browser';

let initialized = false;

export function initFullStory() {
  const orgId = import.meta.env.VITE_FULLSTORY_ORG_ID;

  if (!orgId) {
    console.warn('FullStory: VITE_FULLSTORY_ORG_ID is not set');
    return;
  }

  if (initialized) return;

  FullStory.init({ orgId, devMode: import.meta.env.DEV });
  initialized = true;
  console.log('FullStory initialized with org:', orgId);
}

export { FullStory };

// .env
// VITE_FULLSTORY_ORG_ID=YOUR_ORG_ID_HERE
```

**Expected result:** The @fullstory/browser package installs without errors. The Bolt WebContainer preview shows 'FullStory initialized' in the browser console when you load your app. No TypeScript errors.

### 2. Initialize FullStory in Your React App

With the @fullstory/browser package installed and lib/fullstory.ts created, you need to call initFullStory once when your app loads. In a React app, the right place is either main.tsx (the entry point, called before React renders) or in the top-level App component using a useEffect with an empty dependency array.

For Vite React apps (Bolt's default), main.tsx is the cleanest option — import and call initFullStory before ReactDOM.createRoot renders. This ensures FullStory is initialized before any user interaction can occur, so no early clicks are missed.

For Next.js apps, place the initialization in a client component — for example a FullStoryProvider component rendered inside the root layout's body. Use 'use client' at the top and a useEffect hook. This is necessary because Next.js renders on the server first, and FullStory requires a browser environment (window, document) to initialize. Calling it directly in a Server Component would crash at build time.

After initialization, FullStory automatically captures all click events, scroll positions, mouse movements, and DOM mutations. You do not need to instrument individual components. The session replay recording starts immediately and is viewable in the FullStory dashboard within a few seconds of your first page view on a published domain. Note: the WebContainer preview URL (the bolt.new iframe URL) is treated differently than a production domain by FullStory's recording logic — some recording features require a properly deployed URL. Deploy to Bolt Cloud or Netlify for full session capture.

```
// main.tsx (Vite React)
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
import { initFullStory } from './lib/fullstory';

initFullStory();

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// components/providers/FullStoryProvider.tsx (Next.js)
'use client';

import { useEffect } from 'react';
import { initFullStory } from '@/lib/fullstory';

export function FullStoryProvider({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    initFullStory();
  }, []);

  return <>{children}</>;
}
```

**Expected result:** Open your Bolt preview and check the browser console. You should see 'FullStory initialized with org: YOUR_ORG_ID'. The FullStory recording indicator may appear in the FullStory dashboard after deploying to a production domain.

### 3. Identify Logged-In Users for Session Attribution

Anonymous session replay is useful, but linking sessions to actual user accounts transforms FullStory into a customer support and debugging superpower. With user identification, you can search FullStory for 'show me all sessions from user@example.com', watch the journey of a specific user who filed a support ticket, and build segments based on user properties like plan tier or account age.

FullStory's identify() function takes a unique user ID as the first argument and an optional object of user variables as the second. The user ID should be the same permanent identifier you use in your database — a UUID, a Supabase user ID, or any stable string. Do not use email as the primary identifier since users can change their email. Pass email as a user variable instead.

Call identify() after your authentication state confirms a logged-in user. In a React app with Supabase Auth, this typically happens in an onAuthStateChange callback or in a useEffect that watches the session. When the user logs out, call FullStory.anonymize() to stop attributing subsequent actions to the previous user.

User variables you can pass include any properties that help segment and filter sessions in the FullStory dashboard. Common examples: plan (free/pro/enterprise), accountId, signupDate, or any feature flags the user has active. These variables become searchable dimensions in FullStory's session search and user segments. The FullStory dashboard will show all sessions for a given user ID under People → Users.

```
// In your auth context or auth hook
import { FullStory } from '@/lib/fullstory';
import { useEffect } from 'react';
import { supabase } from '@/lib/supabase';

export function useAuth() {
  useEffect(() => {
    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      (event, session) => {
        if (event === 'SIGNED_IN' && session?.user) {
          const { user } = session;
          FullStory.identify(user.id, {
            displayName: user.email ?? 'Unknown',
            email: user.email ?? '',
            plan: (user.user_metadata?.plan as string) ?? 'free',
            createdAt: user.created_at,
          });
        }

        if (event === 'SIGNED_OUT') {
          FullStory.anonymize();
        }
      }
    );

    return () => subscription.unsubscribe();
  }, []);
}
```

**Expected result:** After logging in, open FullStory's dashboard and go to People. Your user should appear with the properties you passed. Future sessions from this user will be attributed to their account and searchable by email or user ID.

### 4. Track Custom Events to Enrich Session Data

FullStory's autocapture records all clicks and DOM interactions automatically, but custom events let you add business-context signals that raw DOM events cannot capture. A button click on a 'Purchase' button is captured automatically, but FullStory cannot infer whether that click succeeded, failed, or was abandoned. Custom events let you annotate the session timeline with high-level business events: 'checkout_started', 'payment_failed', 'feature_enabled', 'onboarding_completed'.

Custom events in FullStory use FullStory.event(eventName, properties). Event names should be descriptive camelCase or underscore_separated strings. Properties can include any flat object of strings, numbers, or booleans — nested objects are not supported in FullStory's event schema. Property keys are case-sensitive and should be consistent across events (avoid 'planType' in one event and 'plan_type' in another).

A practical pattern for Bolt apps: track key funnel steps. If your app has a signup → onboarding → activate → upgrade flow, add a FullStory event at each milestone. This creates a queryable funnel in FullStory's dashboard: 'show me all sessions where onboarding_completed fired but upgrade_initiated never fired'. These funnel insights often reveal exactly where users get stuck without needing to interpret raw click replays.

Avoid over-instrumenting. Five to ten meaningful business events are more valuable than 50 low-level technical events. Focus on outcomes (completed, failed, abandoned, upgraded) rather than actions (clicked, typed, scrolled). FullStory's autocapture already handles the action layer.

```
// utils/analytics.ts — centralized event tracking
import { FullStory } from '@/lib/fullstory';

export const track = {
  signupCompleted: (method: 'email' | 'google' | 'github') => {
    FullStory.event('signup_completed', { method });
  },

  checkoutStarted: (plan: string, priceInCents: number) => {
    FullStory.event('checkout_started', { plan, price_cents: priceInCents });
  },

  paymentFailed: (errorCode: string, plan: string) => {
    FullStory.event('payment_failed', { error_code: errorCode, plan });
  },

  featureFirstUse: (featureName: string) => {
    FullStory.event('feature_first_use', { feature_name: featureName });
  },

  onboardingCompleted: (stepsCompleted: number) => {
    FullStory.event('onboarding_completed', { steps_completed: stepsCompleted });
  },
};
```

**Expected result:** Custom events appear on the session timeline in FullStory replays. You can filter sessions by these events in FullStory's segment builder. The FullStory dashboard shows event counts over time under Events & Conversions.

### 5. Access FullStory Session Data via REST API (Advanced)

FullStory's REST API lets you programmatically fetch session data, user session lists, and session replay URLs. This is useful for building internal support dashboards, automatically attaching FullStory replay links to support tickets in Zendesk or Intercom when a user writes in, or triggering workflows based on session signals.

The FullStory REST API uses API key authentication. Generate an API key in FullStory Settings → API Keys → Create New. Give it a descriptive name ('Bolt App Backend') and copy the key immediately — it is only shown once. The base URL is https://api.fullstory.com and the primary endpoints for integration use are the Operations API for batch jobs and the Data Export API for session exports.

Important: FullStory's REST API key gives full access to your session data and must never be exposed to the browser. Call the API exclusively from a Next.js API route where the key lives in process.env (server-side only). Never use VITE_FULLSTORY_API_KEY or any NEXT_PUBLIC_ prefix on this key.

The most common REST API use case is fetching recent sessions for a specific user. This requires a Bolt app with Next.js (not plain Vite) for the server-side API route. When a user contacts support, pass their FullStory user ID to your API route, fetch their recent sessions, and surface the direct replay links to your support team without requiring them to log into FullStory. Note: the REST API, webhooks, and data exports require a FullStory Business or Enterprise plan — the free trial includes all features for 14 days.

```
// app/api/fullstory/sessions/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const userId = searchParams.get('userId');

  if (!userId) {
    return NextResponse.json({ error: 'userId is required' }, { status: 400 });
  }

  const apiKey = process.env.FULLSTORY_API_KEY;
  if (!apiKey) {
    return NextResponse.json({ error: 'FullStory API key not configured' }, { status: 500 });
  }

  try {
    const response = await fetch(
      `https://api.fullstory.com/users/v1/individual/${encodeURIComponent(userId)}/sessions?limit=10`,
      {
        headers: {
          Authorization: `Basic ${Buffer.from(apiKey + ':').toString('base64')}`,
          'Content-Type': 'application/json',
        },
      }
    );

    if (!response.ok) {
      const error = await response.text();
      return NextResponse.json({ error }, { status: response.status });
    }

    const data = await response.json();
    return NextResponse.json(data);
  } catch (error: unknown) {
    const e = error as { message: string };
    return NextResponse.json({ error: e.message }, { status: 500 });
  }
}
```

**Expected result:** GET /api/fullstory/sessions?userId=YOUR_USER_ID returns a JSON list of recent sessions with replay URLs. The API key is never exposed to the browser. Deploy to Netlify or Bolt Cloud and test with a real FullStory user ID to see session links.

## Best practices

- Set devMode: import.meta.env.DEV when initializing FullStory to prevent development sessions from cluttering your analytics data
- Use FullStory.identify() with your database user ID (not email) as the primary identifier so user sessions remain linked even if the user changes their email address
- Create a centralized analytics utility file that wraps FullStory.event() calls — this makes it easy to add or swap analytics providers without touching individual components
- Deploy to a real domain (Bolt Cloud or Netlify) early to verify full session capture works, since the WebContainer preview URL may throttle FullStory recording
- Keep FullStory's REST API key in server-side environment variables only — never use VITE_ or NEXT_PUBLIC_ prefixes for FullStory API keys
- Track 5-10 high-value business events (checkout_started, feature_first_use, onboarding_completed) rather than instrumenting every click — FullStory's autocapture already handles raw interaction recording
- Use FullStory's segment builder to create user cohorts based on session behavior, then compare session replays between cohorts (converted vs. churned) to identify UX improvements

## Use cases

### Add Session Replay to Any Bolt App

You've shipped a Bolt app and users are reporting confusion navigating certain features. Add FullStory to watch actual user sessions and identify exactly where people get lost. FullStory auto-captures everything — no manual instrumentation needed. Watch session replays filtered by users who rage-clicked a button or dropped off at a specific page.

Prompt example:

```
Add FullStory session replay to my Bolt app. Install @fullstory/browser via npm. Create a lib/fullstory.ts file that initializes FullStory with the org ID from VITE_FULLSTORY_ORG_ID environment variable. Call the init function in my app's main entry point (main.tsx or App.tsx). Make sure it only initializes in the browser (not during SSR if applicable). Add a .env entry with a placeholder for VITE_FULLSTORY_ORG_ID.
```

### Identify Users for Personalized Session Replay

When your Bolt app has authentication, link FullStory sessions to specific user accounts. This lets you search for all sessions by a particular user, see the full history of a support request, or filter sessions by user properties like plan tier. FullStory's identify() call connects the anonymous session to a known user ID and optional display name.

Prompt example:

```
After my user logs in, call FullStory's identify function to link the session to the user's account. I'm using Supabase Auth. After the auth state changes and we have a user object, call FullStory.identify(user.id, { displayName: user.email, email: user.email, plan: user.user_metadata?.plan || 'free' }). Import @fullstory/browser and call identify in my AuthContext or wherever I set the user state.
```

### Build a Session Data Dashboard Using the FullStory REST API

FullStory's REST API lets you programmatically fetch session data, user profiles, and session replay URLs. Build an internal admin dashboard in your Bolt app that shows recent sessions, filters by user frustration signals, and generates direct replay links for the support team. The API requires OAuth and must be called server-side to protect your credentials.

Prompt example:

```
Create a Next.js API route at app/api/fullstory/sessions/route.ts that fetches recent sessions from FullStory's REST API. Use the FULLSTORY_API_KEY environment variable (server-side only). Call GET https://api.fullstory.com/operations/v1 to list recent sessions. Create a simple React component that displays the last 10 sessions with user ID, session duration, and a link to the FullStory replay URL. Handle errors gracefully if the API is unavailable.
```

## Troubleshooting

### FullStory initializes without errors in Bolt preview but no sessions appear in the FullStory dashboard

Cause: FullStory's recording system may throttle or skip sessions from localhost and preview URLs (like bolt.new's WebContainer URLs) to avoid polluting analytics with developer traffic. Additionally, devMode: true suppresses recording by design.

Solution: Deploy your app to Bolt Cloud or Netlify to get a real production URL. Register that domain in FullStory Settings → Allowed Domains. For development testing, set devMode: false temporarily and use an incognito window on your deployed URL. Check Settings → Privacy → Excluded URLs to ensure your domain is not blocked.

### TypeError: FullStory is not defined / Cannot read properties of undefined

Cause: The @fullstory/browser package is being imported in a Server Component or before the DOM is available. FullStory requires a browser environment — window and document must exist.

Solution: Ensure initFullStory() is only called inside a useEffect hook (for Next.js) or at the top level of main.tsx (for Vite). Never import or call FullStory in a Next.js Server Component, in getStaticProps, or in any file that runs during server-side rendering.

```
// Correct — only runs in the browser:
useEffect(() => {
  initFullStory();
}, []);

// Wrong — crashes on server:
import { initFullStory } from '@/lib/fullstory';
initFullStory(); // Don't call at module level in Next.js
```

### FullStory.identify() throws 'FullStory is not initialized' error

Cause: identify() is called before FullStory.init() completes, or identify() is called in a component that mounts before the FullStoryProvider that calls init().

Solution: Ensure your FullStoryProvider is a parent component of anything that calls identify(). In Next.js, place FullStoryProvider at the root layout level above all pages and other providers. Add the initialized guard in your initFullStory function and check it before calling identify().

```
// Add a safety check before calling identify:
if (initialized) {
  FullStory.identify(userId, props);
} else {
  console.warn('FullStory not yet initialized — identify skipped');
}
```

### FULLSTORY_API_KEY works in Bolt but the API route returns 401 Unauthorized after deployment

Cause: The FULLSTORY_API_KEY environment variable was set in the Bolt .env file but was not added to the Netlify or Vercel environment variables dashboard before deploying.

Solution: In Netlify: go to Site Settings → Environment Variables → Add Variable, enter FULLSTORY_API_KEY and your key value, then trigger a redeploy. In Vercel: Project Settings → Environment Variables → Add. Never commit API keys to your Git repository.

## Frequently asked questions

### Does FullStory work in Bolt.new's WebContainer preview?

FullStory's JavaScript snippet initializes correctly in the Bolt WebContainer preview and will not throw errors. However, full session recording may be throttled on preview URLs. For verified session capture, deploy your app to Bolt Cloud or Netlify and test on the production URL. The initialization and identify() calls can be developed and tested in the preview.

### Is the FullStory snippet a privacy concern for users?

FullStory automatically masks sensitive form fields (password inputs, credit card fields) using its Privacy by Default settings. You can configure additional masking rules in the FullStory dashboard for fields containing PII. For GDPR compliance, add FullStory to your privacy policy and consider implementing consent-gating that only calls FullStory.init() after the user accepts your cookie/analytics consent banner.

### Can I use FullStory with a Vite (non-Next.js) Bolt project?

Yes. The @fullstory/browser package and the HTML snippet both work in Vite React projects. Use import.meta.env.VITE_FULLSTORY_ORG_ID for the org ID. The REST API integration requires API routes, which are a Next.js feature — for Vite projects, use FullStory's dashboard directly or proxy API calls through a separate backend service. The client-side session replay works identically in both Vite and Next.js.

### How do I prevent FullStory from recording in development but still initialize it?

Pass devMode: true to FullStory.init() during development. This initializes the FullStory object (so FullStory.identify() and FullStory.event() calls don't throw errors) but tells FullStory not to record or send data to the server. Set devMode: import.meta.env.DEV in your init call so it automatically disables recording in development and enables it in production builds.

### Does FullStory capture events inside React portals and modals?

Yes. FullStory captures DOM mutations globally, including content rendered via React portals (modals, dropdowns, toasts) that mount outside the main React tree. FullStory's session replay reconstructs the full DOM state including portal content, so you can watch user interactions with dialogs, date pickers, and overlays in the replay.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/fullstory
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/fullstory
