Add the widget

React & Next.js

Install the widget in an authenticated app, with identity verified server-side.

Install

Add the package, then render <FeatureFast /> once near the root of your app. Pass customerId and a customerHash you compute on your server (see below).

Terminalsh
npm install @featurefast/react
src/App.tsxtsx
import { FeatureFast } from "@featurefast/react";

export function App({ user, customerHash }: {
  user: { id: string; email?: string };
  customerHash: string;
}) {
  return (
    <FeatureFast
      projectId="<your projectId>"
      customerId={user.id}
      customerHash={customerHash}
      customerEmail={user.email}
    />
  );
}

Your projectIdis on the install page in your dashboard. It's public and safe to ship in client-side code.

Next.js (App Router)

FeatureFast uses React hooks, so wrap it in a "use client" module before importing it from the server-rendered RootLayout. Compute the hash on the server and render the widget only for signed-in users.

app/FeatureFastWidget.tsx + app/layout.tsxtsx
// app/FeatureFastWidget.tsx
"use client";
export { FeatureFast } from "@featurefast/react";


// app/layout.tsx
import { createHmac } from "node:crypto";
import { FeatureFast } from "./FeatureFastWidget";
import { auth } from "@/lib/auth";

export default async function RootLayout({
  children,
}: { children: React.ReactNode }) {
  const user = await auth();
  const customerHash = user
    ? createHmac("sha256", process.env.FEATUREFAST_SECRET_KEY!)
        .update(user.id)
        .digest("hex")
    : "";

  return (
    <html lang="en">
      <body>
        {children}
        {user ? (
          <FeatureFast
            projectId="<your projectId>"
            customerId={user.id}
            customerHash={customerHash}
            customerEmail={user.email}
          />
        ) : null}
      </body>
    </html>
  );
}

Compute customerHash server-side

customerHash is HMAC-SHA256(secretKey, customerId), hex-encoded. Compute it on your serverand pass it to the client. Never expose your project's secret key (ff_sk_…) in the browser — it lives only in your server environment as FEATUREFAST_SECRET_KEY.

server/featurefast.tsts
import { createHmac } from "node:crypto";

export function featureFastHash(customerId: string): string {
  const secret = process.env.FEATUREFAST_SECRET_KEY;
  if (!secret) throw new Error("FEATUREFAST_SECRET_KEY not configured");
  return createHmac("sha256", secret).update(customerId).digest("hex");
}

Props

PropTypeRequiredNotes
projectIdstringyesFrom your FeatureFast dashboard.
customerIdstringyesYour stable user id.
customerHashstringyesHMAC-SHA256 of customerId, hex.
customerEmailstringnoOptional, surfaced to the founder.

The component throws synchronously if any required prop is missing.

Content-Security-Policy is the #1 blocker

If your app sends a Content-Security-Policy header (or <meta http-equiv>), you must allow-list FeatureFast in two directives or the browser silently blocks the widget — button.jsfails with “Provisional headers are shown” and the widget never appears.
  • script-src https://cdn.featurefast.ai — loads the widget runtime.
  • connect-src https://<your-deployment>.convex.site — submits prompts to your FeatureFast (Convex) Site URL.

The host ends in .convex.site (not .convex.cloud). Find your Site URL in the dashboard, or run npx convex env get CONVEX_SITE_URL. These directives are additive — merge them into your existing CSP rather than replacing it.

Content-Security-Policytext
script-src  'self' https://cdn.featurefast.ai;
connect-src 'self' https://<your-deployment>.convex.site;
next.config.jsjs
// next.config.js
const csp = [
  "script-src 'self' https://cdn.featurefast.ai",
  "connect-src 'self' https://<your-deployment>.convex.site",
].join("; ");

module.exports = {
  async headers() {
    return [
      {
        source: "/:path*",
        headers: [{ key: "Content-Security-Policy", value: csp }],
      },
    ];
  },
};
Widget still not showing up? The troubleshooting guide has a decision tree and the full error reference.