Skip to main content
Technology & EngineeringForm Survey Services247 lines

Formbricks

Integrate Formbricks open-source surveys for in-app, website, and link-based

Quick Summary23 lines
You are an expert at integrating Formbricks, the open-source survey platform, into web applications. You configure in-app surveys triggered by user actions, collect website feedback via no-code widgets, and manage response data through the REST API.

## Key Points

- **Showing surveys without event triggers** — random pop-ups annoy users and tank completion rates.
- **Forgetting to call `formbricks.logout()`** on sign-out — the next user inherits the previous user's identity and attributes.
- **Not setting user attributes** — you lose the ability to target surveys by plan, role, or tenure.
- **Running the JS SDK in SSR** — Formbricks requires `window`. Initialize only on the client side inside `useEffect`.
- Collecting in-app feedback triggered by specific user actions (post-onboarding, feature usage, churn signals).
- Running NPS or CSAT surveys inside your product without redirecting users externally.
- Self-hosting a survey platform for GDPR compliance and full data ownership.
- A/B testing onboarding flows by surveying different user segments.
- Building product analytics feedback loops where survey data feeds into your data warehouse.

## Quick Example

```bash
NEXT_PUBLIC_FORMBRICKS_ENV_ID=clu...
NEXT_PUBLIC_FORMBRICKS_API_HOST=https://app.formbricks.com
FORMBRICKS_API_KEY=fb_...
```
skilldb get form-survey-services-skills/FormbricksFull skill: 247 lines
Paste into your CLAUDE.md or agent config

Formbricks Skill

You are an expert at integrating Formbricks, the open-source survey platform, into web applications. You configure in-app surveys triggered by user actions, collect website feedback via no-code widgets, and manage response data through the REST API.

Core Philosophy

Event-Triggered Surveys

Formbricks excels at showing the right survey at the right moment. Trigger surveys based on user actions (e.g., completed onboarding, clicked upgrade, visited pricing page) rather than showing them randomly. Use the track() method to fire custom events and tie them to survey display rules.

Privacy-First Data Collection

Formbricks is self-hostable and GDPR-friendly by design. When self-hosting, all data stays on your infrastructure. Use the userId parameter to associate responses with known users without leaking PII to third parties. For anonymous surveys, omit userId entirely.

Progressive Survey Delivery

Avoid survey fatigue by using Formbricks' built-in targeting: show surveys only to specific user segments, limit display frequency, and stop showing a survey once a user has responded. Configure these rules in the dashboard or via API attributes.

Setup

Install the JavaScript SDK for in-app surveys:

// npm install @formbricks/js

import formbricks from "@formbricks/js";

formbricks.init({
  environmentId: process.env.NEXT_PUBLIC_FORMBRICKS_ENV_ID!,
  apiHost: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST!, // https://app.formbricks.com or self-hosted URL
  userId: currentUser?.id,          // optional: for identified users
  attributes: {                      // optional: for targeting
    plan: currentUser?.plan,
    role: currentUser?.role,
  },
});

For the REST API:

const FB_API_HOST = process.env.FORMBRICKS_API_HOST;
const FB_API_KEY = process.env.FORMBRICKS_API_KEY;

async function fbFetch<T>(path: string, init?: RequestInit): Promise<T> {
  const res = await fetch(`${FB_API_HOST}/api/v1${path}`, {
    ...init,
    headers: {
      "x-api-key": FB_API_KEY!,
      "Content-Type": "application/json",
      ...init?.headers,
    },
  });
  if (!res.ok) throw new Error(`Formbricks ${res.status}: ${await res.text()}`);
  const json = await res.json();
  return json.data as T;
}

Environment variables:

NEXT_PUBLIC_FORMBRICKS_ENV_ID=clu...
NEXT_PUBLIC_FORMBRICKS_API_HOST=https://app.formbricks.com
FORMBRICKS_API_KEY=fb_...

Key Patterns

Track Custom Events for Survey Triggers

Do this — fire events that trigger contextual surveys:

// Track when a user completes onboarding
async function completeOnboarding(userId: string) {
  await saveOnboardingComplete(userId);
  formbricks.track("onboarding_completed");
}

// Track when a user hits a feature wall
function handleFeatureGate(feature: string) {
  formbricks.track("feature_gate_hit", { feature });
  showUpgradeModal();
}

// Track page-specific events
function ProductPage() {
  useEffect(() => {
    formbricks.track("viewed_pricing_page");
  }, []);
  return <PricingContent />;
}

Not this — showing surveys on every page load without any targeting or event context.

Set User Attributes for Targeting

Do this — update attributes so surveys reach the right segments:

// Set attributes when user context changes
async function onPlanUpgrade(newPlan: string) {
  formbricks.setAttribute("plan", newPlan);
  formbricks.setAttribute("upgraded_at", new Date().toISOString());
}

// Set attributes on login
function onUserLogin(user: { id: string; plan: string; role: string; createdAt: string }) {
  formbricks.setUserId(user.id);
  formbricks.setAttribute("plan", user.plan);
  formbricks.setAttribute("role", user.role);
  formbricks.setAttribute("signup_date", user.createdAt);
}

// Reset on logout
function onLogout() {
  formbricks.logout();
}

Not this — showing every survey to every user without segmentation, causing survey fatigue.

Fetch and Analyze Responses via API

Do this — retrieve responses with typed interfaces:

interface FormbricksResponse {
  id: string;
  surveyId: string;
  finished: boolean;
  createdAt: string;
  updatedAt: string;
  person: { id: string; attributes: Record<string, string> } | null;
  data: Record<string, string | number | string[]>;
}

async function getSurveyResponses(surveyId: string): Promise<FormbricksResponse[]> {
  return fbFetch<FormbricksResponse[]>(`/management/responses?surveyId=${surveyId}`);
}

// Calculate NPS from responses
function calculateNPS(responses: FormbricksResponse[], questionId: string): number {
  const scores = responses
    .map((r) => Number(r.data[questionId]))
    .filter((n) => !isNaN(n));
  const promoters = scores.filter((s) => s >= 9).length;
  const detractors = scores.filter((s) => s <= 6).length;
  return Math.round(((promoters - detractors) / scores.length) * 100);
}

Not this — exporting CSV manually from the dashboard for every analysis run.

Common Patterns

Create a Survey via API

interface CreateSurveyPayload {
  name: string;
  type: "app" | "website" | "link";
  questions: Array<{
    type: "openText" | "multipleChoiceSingle" | "multipleChoiceMulti" | "rating" | "nps" | "cta";
    headline: string;
    required: boolean;
    choices?: Array<{ label: string }>;
    scale?: "number" | "star" | "smiley";
    range?: 5 | 10;
  }>;
}

const survey = await fbFetch<{ id: string }>("/management/surveys", {
  method: "POST",
  body: JSON.stringify({
    name: "Feature Feedback",
    type: "app",
    questions: [
      { type: "rating", headline: "How useful is this feature?", required: true, scale: "star", range: 5 },
      { type: "openText", headline: "Any suggestions?", required: false },
    ],
  } satisfies CreateSurveyPayload),
});

React Integration with Next.js App Router

// app/providers.tsx
"use client";
import { useEffect } from "react";
import formbricks from "@formbricks/js";

export function FormbricksProvider({ userId, attributes }: { userId?: string; attributes?: Record<string, string> }) {
  useEffect(() => {
    formbricks.init({
      environmentId: process.env.NEXT_PUBLIC_FORMBRICKS_ENV_ID!,
      apiHost: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST!,
      ...(userId ? { userId } : {}),
      ...(attributes ? { attributes } : {}),
    });
  }, [userId]);

  return null;
}

Webhook Integration

// Formbricks sends webhooks for response events
interface FormbricksWebhookPayload {
  event: "responseCreated" | "responseUpdated" | "responseFinished";
  data: FormbricksResponse;
}

app.post("/webhooks/formbricks", async (req, res) => {
  const payload = req.body as FormbricksWebhookPayload;
  if (payload.event === "responseFinished") {
    await syncToAnalytics(payload.data);
  }
  res.status(200).send("OK");
});

Anti-Patterns

  • Showing surveys without event triggers — random pop-ups annoy users and tank completion rates.
  • Forgetting to call formbricks.logout() on sign-out — the next user inherits the previous user's identity and attributes.
  • Not setting user attributes — you lose the ability to target surveys by plan, role, or tenure.
  • Running the JS SDK in SSR — Formbricks requires window. Initialize only on the client side inside useEffect.

When to Use

  • Collecting in-app feedback triggered by specific user actions (post-onboarding, feature usage, churn signals).
  • Running NPS or CSAT surveys inside your product without redirecting users externally.
  • Self-hosting a survey platform for GDPR compliance and full data ownership.
  • A/B testing onboarding flows by surveying different user segments.
  • Building product analytics feedback loops where survey data feeds into your data warehouse.

Install this skill directly: skilldb add form-survey-services-skills

Get CLI access →