# How to Integrate Bolt.new with UserTesting

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

## TL;DR

UserTesting does not offer a public REST API — integration with Bolt.new means using UserTesting alongside your app rather than embedding it technically. The most practical approach is sharing your deployed Bolt app URL with UserTesting testers for unmoderated testing sessions. For in-app feedback, build a custom feedback widget in Bolt (star rating, NPS survey, open text) or add FullStory for automatic session replay instead of waiting for scheduled UserTesting sessions.

## UserTesting with Bolt.new: Honest Guidance on What's Possible and What Actually Works

UserTesting is a premium UX research platform that provides video recordings of real users navigating your product while narrating their thoughts. It is genuinely valuable for understanding why users struggle with specific workflows — a single UserTesting session often reveals usability issues that months of analytics data obscures. However, UserTesting's API access is enterprise-only and not publicly documented, which means there is no practical code-based integration with Bolt.new apps.

The real-world workflow is straightforward: deploy your Bolt app to Bolt Cloud, Netlify, or any hosting platform to get a public URL, then share that URL with UserTesting when setting up a test. UserTesting testers receive the link, visit your app, and complete tasks you specify in the UserTesting platform. You watch the recordings in the UserTesting dashboard. The 'integration' is the shared URL, not an API connection.

For Bolt developers who want automated user feedback without UserTesting's enterprise pricing ($49,000+/year for large teams, though self-serve plans are available starting around $299/month), there are better alternatives that actually connect to Bolt apps programmatically. This tutorial covers three complementary approaches: deploying Bolt apps in a UserTesting-compatible way, building custom in-app feedback widgets that capture qualitative feedback continuously, and using FullStory's session replay for the automated observation capabilities that UserTesting provides through scheduled sessions.

## Before you start

- A deployed Bolt app with a public URL (Bolt Cloud, Netlify, or Vercel) — UserTesting requires a live URL, not a WebContainer preview
- A UserTesting account (self-serve plans start at ~$299/month; enterprise plans for larger teams)
- For the custom NPS widget: a Supabase database with authentication to store and associate feedback with users
- For Typeform embed: a Typeform account with a survey created and a form ID

## Step-by-step guide

### 1. Deploy Your Bolt App and Prepare It for External Testing

UserTesting requires a publicly accessible URL that panel testers can visit without creating accounts or knowing your internal systems. Before setting up a UserTesting study, deploy your Bolt app to a production hosting environment.

Bolt Cloud is the simplest option: click 'Publish' in the top-right corner of the Bolt editor. Your app deploys to a .bolt.host URL with SSL automatically. You can also connect a custom domain in Bolt's Settings. Netlify is the alternative: go to Settings → Applications → Netlify → Connect, then click Publish. Both give you a stable HTTPS URL you can share with UserTesting.

For UserTesting sessions, consider whether you want testers to use real functionality or a demo mode. Real functionality requires testers to create accounts, which adds friction and can fail if your auth flow is confusing. A demo mode with pre-filled data and a guest login is often better for testing UI flows. Create a demo user account in your Supabase Auth with a simple password and include the credentials in your UserTesting task instructions.

Verify your deployed app is ready for external users: check that all pages load without errors, navigation links work, mobile responsive layout functions correctly, and error messages are user-friendly. UserTesting session recordings that surface technical error messages ('MongooseServerSelectionError') rather than helpful UI messages create noise in your research. Invest 30 minutes testing your own deployment before sending it to UserTesting panelists.

Note: Bolt's WebContainer preview URL (the development URL inside Bolt's editor) is not suitable for UserTesting. The preview URL is session-specific, requires Bolt login context, and will not load reliably for external testers. Always use your deployed URL.

```
// app/api/demo/reset/route.ts
import { NextResponse } from 'next/server';
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY! // service role to bypass RLS
);

const DEMO_USER_ID = process.env.DEMO_USER_ID!;

export async function POST() {
  try {
    // Delete all demo user's created items
    await supabase
      .from('user_projects')
      .delete()
      .eq('user_id', DEMO_USER_ID);

    // Reset demo user profile to defaults
    await supabase
      .from('profiles')
      .update({ onboarding_complete: false, settings: {} })
      .eq('id', DEMO_USER_ID);

    return NextResponse.json({ success: true, message: 'Demo reset complete' });
  } catch (error: unknown) {
    const e = error as { message: string };
    return NextResponse.json({ error: e.message }, { status: 500 });
  }
}
```

**Expected result:** Your Bolt app is deployed to a public URL (e.g., yourapp.bolt.host or yourapp.netlify.app). The demo login flow works without manual account creation. UserTesting testers can access all features using the demo credentials you provide in the task instructions.

### 2. Build a Custom NPS Survey Widget for Continuous Feedback

While UserTesting provides scheduled deep-dive sessions, continuous in-app feedback collection fills the gaps between research rounds. A Net Promoter Score (NPS) survey is the most efficient single-question feedback mechanism: asking users 'How likely are you to recommend this to a friend?' on a 0-10 scale takes 30 seconds and gives you actionable segmentation data (Detractors 0-6, Passives 7-8, Promoters 9-10).

The best time to show an NPS survey is after a user experiences enough of your product to have an informed opinion — typically after 3-5 active sessions, after completing onboarding, or after reaching a key value moment (their first successful export, first project shared, first successful API call). Showing it too early (first visit) collects meaningless data.

Display the NPS survey as a modal or slide-up panel to avoid disrupting the current workflow. Use localStorage to ensure it only shows once per user — checking both localStorage and your database prevents it from reappearing after a user clears browser storage. Store responses in Supabase with the user ID, score, comment, app version, and the page/feature they were using when the survey appeared.

Review NPS responses weekly in your Supabase dashboard. Create a simple Supabase query: SELECT score, comment, created_at FROM nps_responses ORDER BY created_at DESC LIMIT 50. For qualitative analysis, sort by score and read the comments in each segment — Detractor comments (0-6) tell you what to fix, Promoter comments (9-10) tell you what to amplify in your marketing.

```
// app/api/nps/route.ts
import { NextResponse } from 'next/server';
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
import { cookies } from 'next/headers';

export async function POST(request: Request) {
  const supabase = createRouteHandlerClient({ cookies });
  const { data: { user } } = await supabase.auth.getUser();

  if (!user) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const body = await request.json() as { score: number; comment?: string; page_url?: string };

  if (typeof body.score !== 'number' || body.score < 0 || body.score > 10) {
    return NextResponse.json({ error: 'Score must be 0-10' }, { status: 400 });
  }

  const { error } = await supabase.from('nps_responses').insert({
    user_id: user.id,
    score: body.score,
    comment: body.comment ?? null,
    page_url: body.page_url ?? '',
  });

  if (error) {
    return NextResponse.json({ error: error.message }, { status: 500 });
  }

  return NextResponse.json({ success: true });
}
```

**Expected result:** The NPS survey appears after 5 minutes of use for users who haven't been surveyed. On submission, responses are stored in Supabase with the user ID, score, and comment. The LocalStorage flag prevents the survey from showing again. Query nps_responses in Supabase Table Editor to verify data is being captured.

### 3. Build a Contextual Session Feedback Form

UserTesting sessions give deep qualitative insight but require users to be available for a scheduled session. A contextual feedback widget that appears after specific user actions captures feedback in the moment — when the user's experience is freshest and most accurate.

The most effective contextual feedback trigger points: after an error (the user just hit a bug — ask immediately what they were trying to do), after completing a complex flow (onboarding, checkout, project setup), after the user has been idle for 5+ minutes on a page (they may be stuck), or as an exit intent overlay when the user moves to close the tab.

For a simple implementation, create a floating feedback button (a question mark icon in the bottom-right corner) that users can click any time to leave feedback. This respects user autonomy better than popup surveys and consistently captures feedback from the most engaged users — people who are frustrated enough or impressed enough to proactively report their experience.

Store feedback with context: the page URL, user ID, timestamp, app version (from environment variables), and the user's session duration. This context transforms raw feedback into actionable insights. 'The export button doesn't work' is useful; 'The export button doesn't work on the /projects/[id]/settings page, reported by a Pro plan user after 18 minutes in the session' is a bug report your engineering team can act on immediately.

Note: Bolt's WebContainer cannot receive incoming webhooks, but this feedback form submits to a Next.js API route — an outbound POST that works in both the preview and after deployment.

```
// components/FeedbackWidget.tsx (simplified structure)
'use client';

import { useState } from 'react';

type Sentiment = 'happy' | 'neutral' | 'frustrated';

export function FeedbackWidget() {
  const [open, setOpen] = useState(false);
  const [message, setMessage] = useState('');
  const [sentiment, setSentiment] = useState<Sentiment>('neutral');
  const [submitted, setSubmitted] = useState(false);

  const submit = async () => {
    await fetch('/api/feedback', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        message,
        sentiment,
        page_url: window.location.href,
      }),
    });
    setSubmitted(true);
    setTimeout(() => {
      setOpen(false);
      setSubmitted(false);
      setMessage('');
    }, 2000);
  };

  return (
    <div style={{ position: 'fixed', bottom: 24, right: 24, zIndex: 1000 }}>
      {!open && (
        <button
          onClick={() => setOpen(true)}
          style={{ borderRadius: '50%', width: 48, height: 48 }}
          aria-label="Give feedback"
        >
          💬
        </button>
      )}
      {open && (
        <div style={{ background: 'white', border: '1px solid #e2e8f0', borderRadius: 12, padding: 16, width: 300 }}>
          {submitted ? (
            <p>Thank you for your feedback!</p>
          ) : (
            <>
              <textarea
                value={message}
                onChange={(e) => setMessage(e.target.value)}
                placeholder="What's on your mind?"
                rows={4}
                style={{ width: '100%', marginBottom: 8 }}
              />
              <div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
                {(['happy', 'neutral', 'frustrated'] as Sentiment[]).map((s) => (
                  <button key={s} onClick={() => setSentiment(s)}
                    style={{ fontWeight: sentiment === s ? 'bold' : 'normal' }}>
                    {s === 'happy' ? '😊' : s === 'neutral' ? '😐' : '😤'}
                  </button>
                ))}
              </div>
              <button onClick={submit} disabled={!message.trim()}>Send</button>
              <button onClick={() => setOpen(false)} style={{ marginLeft: 8 }}>Cancel</button>
            </>
          )}
        </div>
      )}
    </div>
  );
}
```

**Expected result:** The floating feedback button appears on every page. Users can submit feedback with a sentiment rating. Responses appear in the Supabase 'feedback' table with the page URL and timestamp. The widget closes automatically after submission.

### 4. Integrate FullStory as an Automated Alternative to UserTesting

For Bolt developers who want automated session observation without UserTesting's scheduling overhead and per-session costs, FullStory provides the closest equivalent: actual video recordings of user sessions captured automatically in production. Where UserTesting shows you 5-10 recruited users completing prescribed tasks, FullStory shows you thousands of real sessions from actual users doing actual things.

The trade-off: UserTesting provides narration (users verbalize what they're thinking), which reveals user mental models and confusion points that silent session replay cannot surface. FullStory shows you what users do; UserTesting shows you why. For a complete UX research program, both are valuable. For early-stage Bolt apps on a budget, FullStory's automatic capture often reveals the same usability issues as UserTesting studies.

Adding FullStory to your Bolt app requires the @fullstory/browser npm package and your FullStory Org ID. Install the package, initialize with your org ID in the app entry point, and FullStory begins recording all sessions automatically. The FullStory dashboard provides session search (find sessions where users rage-clicked, hit errors, or spent more than 5 minutes on a specific page) and DX Data (ML-powered frustration signal detection) that mimics some of UserTesting's insight-generation without manual session scheduling.

Note: FullStory's browser SDK sends data directly from the user's browser to FullStory's servers over HTTPS, so it works in Bolt's WebContainer during development for initialization testing. Full session recording activates on your deployed production URL.

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

let initialized = false;

export function initFullStory(orgId: string) {
  if (initialized || !orgId) return;
  FullStory.init({
    orgId,
    devMode: import.meta.env.DEV,
  });
  initialized = true;
}

export function identifyUser(userId: string, email: string, plan = 'free') {
  if (!initialized) return;
  FullStory.identify(userId, {
    displayName: email,
    email,
    plan,
  });
}

export function anonymizeUser() {
  if (!initialized) return;
  FullStory.anonymize();
}

// In App.tsx:
// useEffect(() => {
//   initFullStory(import.meta.env.VITE_FULLSTORY_ORG_ID);
// }, []);
```

**Expected result:** FullStory initializes without errors in the Bolt preview. After deploying and receiving real users, session recordings appear in the FullStory dashboard. You can search sessions by user ID, URL, and frustration signals. This provides continuous passive observation between scheduled UserTesting sessions.

## Best practices

- Always deploy to a production URL (Bolt Cloud or Netlify) before UserTesting sessions — the WebContainer preview URL is not suitable for external testers
- Create a dedicated demo account with pre-populated data for UserTesting sessions rather than asking testers to create real accounts
- Show NPS surveys after users experience value moments (completed onboarding, first successful action), not immediately on first visit
- Store user feedback with context (page URL, session duration, user plan) to make raw feedback actionable without requiring manual follow-up
- Combine UserTesting (scheduled qualitative) with FullStory (automated quantitative session replay) for a complete UX research program
- Reset demo data between UserTesting sessions using a /api/demo/reset endpoint to ensure each tester starts with a clean state
- Review NPS responses weekly — sort by Detractor scores first to identify the highest-impact UX issues to fix before your next UserTesting round

## Use cases

### Conduct Unmoderated User Testing on a Deployed Bolt App

You've built a Bolt app and want real users to test it before launch. Deploy to Bolt Cloud or Netlify to get a stable URL. Create a UserTesting study with specific tasks (find the settings page, complete a purchase, invite a team member) and share the URL with the UserTesting panel. Watch recordings in the UserTesting dashboard. No API integration required — UserTesting works with any publicly accessible URL.

Prompt example:

```
Help me prepare my Bolt app for UserTesting. Make sure the app is production-ready for external testers: check that all navigation flows work, add loading states for slow operations, ensure error messages are user-friendly (not technical error codes), and add a visible 'demo mode' banner so testers know they're on a test version. Also create a separate /demo route with pre-populated sample data so testers don't need to create accounts or enter real information.
```

### Build an In-App NPS Survey Widget

Instead of waiting for scheduled UserTesting sessions, collect continuous feedback from real users with a Net Promoter Score widget that appears after key actions. Show users a 0-10 scale asking 'How likely are you to recommend this app to a friend?' and an optional text field for their reason. Save responses to your database and review them weekly. This provides continuous qualitative feedback between formal UserTesting rounds.

Prompt example:

```
Build an NPS (Net Promoter Score) survey widget for my Bolt app. Create a NpsSurvey React component that appears as a modal overlay after a user has been active for 5 minutes (use a setTimeout). Show a 0-10 scale for 'How likely are you to recommend this to a friend?' and an optional text input for their reasoning. On submit, POST to /api/nps with the user's ID (from Supabase Auth), their score (0-10), their comment text, and the current page URL. Store in a Supabase 'nps_responses' table. Show the widget only once per user (check localStorage for 'nps_shown').
```

### Embed Typeform for Contextual User Research

Typeform provides a conversational survey experience with branching logic that UserTesting lacks. Embed a Typeform survey as a popup or side panel in your Bolt app, triggered after specific user actions (completing onboarding, hitting an error, reaching a certain feature). Typeform's Webhooks deliver responses to your Next.js API route in real-time, populating your user research database automatically.

Prompt example:

```
Embed a Typeform survey popup in my Bolt app that appears after a user completes onboarding. Get the Typeform form ID from my Typeform account (form URL format: typeform.com/to/FORM_ID). Create a TypeformEmbed React component that renders the Typeform widget using @typeform/embed-react. Show it after the onboarding completion event fires. Include a hidden field that passes the user's ID to the Typeform response for matching feedback to user accounts.
```

## Troubleshooting

### UserTesting testers cannot access my Bolt app — they see an error or blank page

Cause: The Bolt WebContainer preview URL was shared instead of the deployed URL, or the app was deployed but has a runtime error that only surfaces for new users (missing auth cookies, pre-populated state that development depended on).

Solution: Always share your deployed URL (yourapp.bolt.host or yourapp.netlify.app) with UserTesting — never the preview URL. Test the deployed URL in an incognito window without any auth cookies to simulate a new user's experience. Check the browser console for JavaScript errors on first load.

### NPS survey appears multiple times for the same user

Cause: The localStorage check for 'nps_shown' only works per-browser. If the user clears storage, uses a different browser, or is on mobile, they see the survey again.

Solution: Add a server-side check in addition to localStorage: query your nps_responses table for an existing response from the current user before showing the survey. The API route can check the database and return { already_responded: true } if a response exists.

```
// Add to your NPS check logic:
const checkNpsStatus = async (userId: string) => {
  const { data } = await supabase
    .from('nps_responses')
    .select('id')
    .eq('user_id', userId)
    .limit(1)
    .single();
  return !!data; // true = already responded
};
```

### Feedback widget POSTs to /api/feedback but gets 401 Unauthorized

Cause: The /api/feedback route requires authentication via Supabase Auth, but the request doesn't include the auth cookie because the component is making a plain fetch() without the credentials: 'include' option.

Solution: The createRouteHandlerClient from @supabase/auth-helpers-nextjs reads cookies from the request. Ensure the fetch call includes credentials: 'include' (or use the Supabase client from the browser-side which handles cookies automatically). Alternatively, make user_id nullable in your feedback table and accept anonymous feedback.

```
// Include credentials in the fetch call:
await fetch('/api/feedback', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  credentials: 'include', // Include auth cookies
  body: JSON.stringify({ message, sentiment, page_url }),
});
```

## Frequently asked questions

### Does UserTesting have an API I can use in my Bolt.new app?

UserTesting's API is enterprise-only and not publicly documented. There is no publicly available API for creating studies, managing panelists, or retrieving session recordings programmatically. The practical integration with Bolt.new is sharing your deployed app URL as the test URL in UserTesting's study setup dashboard — no code integration required.

### Can UserTesting record sessions from Bolt's WebContainer preview?

No. UserTesting panelists open your URL in their own browser. The Bolt WebContainer preview URL is session-specific and requires Bolt authentication context to load — external testers cannot access it. Always deploy to Bolt Cloud, Netlify, or another hosting platform and share the deployed URL with UserTesting. Test the URL yourself in an incognito window before sharing it with testers.

### What's the difference between UserTesting and FullStory?

UserTesting is a UX research platform where recruited participants complete tasks in scheduled sessions while narrating their thoughts — providing rich qualitative insight about user mental models. FullStory is an automatic session recording tool that captures all real user sessions passively. UserTesting shows you why users struggle; FullStory shows you that users struggle and where. For early-stage Bolt apps, FullStory is often more practical than UserTesting due to its lower cost and automatic operation.

### How do I collect user feedback without UserTesting?

Build a custom feedback widget in your Bolt app (floating button → slide-up form → Supabase table), add an NPS survey that triggers after users experience value, or embed Typeform for structured surveys. These approaches collect continuous feedback from real users without the scheduling overhead of UserTesting. Combine with FullStory session replay for qualitative observation and you cover most of what UserTesting provides at lower cost.

### What should I include in UserTesting task instructions for a Bolt app?

Provide clear, task-focused instructions without telling users how to complete the task (you want to observe their natural approach). Include login credentials for your demo account, specific tasks to attempt ('find the export function', 'upgrade to the Pro plan'), and any background context the tester needs. Keep tasks to 3-5 to fit within UserTesting's typical 20-30 minute session. Avoid leading questions like 'how easy is the checkout button to find?' — say instead 'please purchase a Pro subscription'.

---

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