TypeScript SDK for Fdback user feedback platform
npm install @fdback.io/sdkOfficial TypeScript SDK for integrating Fdback user feedback into your application.
- Type-safe - Full TypeScript support with detailed type definitions
- Isomorphic - Works in Node.js and browsers
- Secure - HMAC-SHA256 signed authentication
- Framework support - First-class React and Next.js integrations
- Lightweight - ~3.7KB core, ~5KB for framework bindings
``bash`
npm install @fdback.io/sdkor
pnpm add @fdback.io/sdkor
yarn add @fdback.io/sdk
Get your Workspace ID and Workspace Secret from your Fdback dashboard under Settings > SDK Credentials.
`typescript
import { FdbackServer } from "@fdback.io/sdk";
const fdback = new FdbackServer({
workspaceId: process.env.FDBACK_WORKSPACE_ID!,
workspaceSecret: process.env.FDBACK_WORKSPACE_SECRET!,
});
// Create signed login data for a user
const signedData = await fdback.createSignedLoginData({
email: "user@example.com",
name: "John Doe",
avatar: "https://example.com/avatar.jpg", // optional
});
`
`typescript
import { FdbackClient } from "@fdback.io/sdk";
const fdback = new FdbackClient({
workspaceId: "your-workspace-id",
});
// Open with signed data from your server
fdback.open(signedData, { mode: "popup" });
`
> Note: FdbackServer and FdbackClient are aliases for the same Fdback class. The naming makes it clearer which environment you're in.
---
The workspace secret is used to sign requests with HMAC-SHA256. Never expose the secret in client-side code.
| Environment | Approach |
|-------------|----------|
| Server-side | Use workspaceSecret directly |
| Client-side | Fetch signed data from your backend API |
---
`typescript
import { Fdback } from "@fdback.io/sdk";
const fdback = new Fdback({
workspaceId: string; // Required
workspaceSecret?: string; // Server-side only
baseUrl?: string; // Default: "https://app.fdback.io"
});
`
#### Methods
| Method | Environment | Description |
|--------|-------------|-------------|
| createSignedLoginData(user) | Server | Create signed auth data for a user |getLoginUrl(user)
| | Server | Get a signed login URL for redirects |open(signedData, options?)
| | Browser | Open feedback board in popup/tab |openDirect(user, options?)
| | Browser | Sign and open (requires secret - not recommended) |getBoardUrl()
| | Both | Get the public board URL |isServerMode()
| | Both | Check if secret is configured |
`typescript
interface FdbackUser {
email: string; // Required
name?: string; // Display name
avatar?: string; // Avatar URL
}
interface SignedLoginData {
url: string;
headers: {
"x-workspace-id": string;
"x-timestamp": string;
"x-signature": string;
};
body: string;
expiresAt: number; // Expires in 5 minutes
}
interface OpenOptions {
mode?: "popup" | "tab" | "redirect"; // Default: "popup"
width?: number; // Default: 800
height?: number; // Default: 700
}
`
---
`bash`
npm install @fdback.io/sdk react
`tsx
// app/providers.tsx or app/layout.tsx
import { FdbackProvider } from "@fdback.io/sdk/react";
export function Providers({ children }: { children: React.ReactNode }) {
return (
signEndpoint="/api/fdback/sign"
>
{children}
);
}
`
The easiest way to add feedback - a ready-to-use button:
`tsx
"use client";
import { FeedbackButton } from "@fdback.io/sdk/react";
export function Header() {
const user = useCurrentUser(); // Your auth hook
return (
className="btn btn-primary"
onOpen={(win) => console.log("Opened!", win)}
onError={(err) => console.error(err)}
>
Give Feedback
);
}
`
For custom implementations:
`tsx
"use client";
import { useFdbackLogin } from "@fdback.io/sdk/react";
export function CustomFeedback() {
const { login, isLoading, error } = useFdbackLogin();
const handleClick = async () => {
await login(
{ email: "user@example.com", name: "John" },
{ mode: "popup" }
);
};
return (
);
}
`
Access the Fdback context directly:
`tsx
"use client";
import { useFdback } from "@fdback.io/sdk/react";
export function FeedbackStatus() {
const { isOpen, getBoardUrl, workspaceId } = useFdback();
return (
Workspace: {workspaceId}
Board URL: {getBoardUrl()}
Popup open: {isOpen ? "Yes" : "No"}
$3
Embed the full widget (feedback, roadmap, changelog tabs) directly in your React app:
`tsx
"use client";
import { FdbackWidget } from "@fdback.io/sdk/react";// Uncontrolled - uses default floating launcher button
export function App() {
return (
workspaceId="your-workspace-id"
mode="full"
tabs={["feedback", "roadmap", "changelog"]}
theme="dark" // or "light" or "auto"
primaryColor="#6366f1"
/>
);
}
// Controlled - use your own trigger button
export function CustomTrigger() {
const [open, setOpen] = useState(false);
return (
<>
workspaceId="your-workspace-id"
open={open}
onOpenChange={setOpen}
showLauncher={false}
/>
>
);
}
`#### FdbackWidget Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
|
workspaceId | string | required | Your workspace ID |
| mode | "simple" \| "full" | "simple" | Simple = feedback only, Full = tabs |
| tabs | ("feedback" \| "roadmap" \| "changelog")[] | ["feedback"] | Tabs to show in full mode |
| theme | "auto" \| "dark" \| "light" | "auto" | Theme mode |
| primaryColor | string | - | Hex color for accent |
| position | "bottom-right" \| "bottom-left" \| "top-right" \| "top-left" | "bottom-right" | Launcher position |
| open | boolean | - | Controlled open state |
| onOpenChange | (open: boolean) => void | - | Open state change callback |
| showLauncher | boolean | true | Show the floating launcher button |
| signEndpoint | string | - | Auth endpoint URL |$3
| Export | Type | Description |
|--------|------|-------------|
|
FdbackProvider | Component | Context provider |
| FdbackWidget | Component | Embeddable widget with tabs |
| FeedbackButton | Component | Ready-to-use button |
| useFdback | Hook | Access context (client, isOpen, etc.) |
| useFdbackLogin | Hook | Login + open with loading/error state |
| useWidget | Hook | Programmatic widget control |---
Next.js Integration
`bash
npm install @fdback.io/sdk server-only
`The
/next subpath includes server-only protection to prevent accidental client-side imports.$3
Create a route that handles signing:
`typescript
// app/api/fdback/sign/route.ts
import { createSignHandler } from "@fdback.io/sdk/next";export const POST = createSignHandler({
workspaceId: process.env.FDBACK_WORKSPACE_ID!,
workspaceSecret: process.env.FDBACK_WORKSPACE_SECRET!,
});
`With authentication:
`typescript
// app/api/fdback/sign/route.ts
import { createSignHandler } from "@fdback.io/sdk/next";
import { auth } from "@/server/auth";export const POST = createSignHandler({
workspaceId: process.env.FDBACK_WORKSPACE_ID!,
workspaceSecret: process.env.FDBACK_WORKSPACE_SECRET!,
authorize: async () => {
const session = await auth();
return !!session?.user;
},
getUser: async () => {
const session = await auth();
if (!session?.user?.email) return null;
return {
email: session.user.email,
name: session.user.name ?? undefined,
avatar: session.user.image ?? undefined,
};
},
});
`$3
Create a server action for use with React Server Components:
`typescript
// lib/fdback.ts
"use server";
import { createSignAction } from "@fdback.io/sdk/next";
import { auth } from "@/server/auth";const _signAction = createSignAction({
workspaceId: process.env.FDBACK_WORKSPACE_ID!,
workspaceSecret: process.env.FDBACK_WORKSPACE_SECRET!,
});
// Wrap with auth check
export async function signFdbackLogin() {
const session = await auth();
if (!session?.user?.email) {
return { success: false as const, error: "Unauthorized" };
}
return _signAction({
email: session.user.email,
name: session.user.name ?? undefined,
avatar: session.user.image ?? undefined,
});
}
`Use in client component:
`tsx
"use client";
import { signFdbackLogin } from "@/lib/fdback";
import { useFdback } from "@fdback.io/sdk/react";export function FeedbackButton() {
const { open } = useFdback();
const handleClick = async () => {
const result = await signFdbackLogin();
if (result.success) {
open(result.data);
} else {
console.error(result.error);
}
};
return ;
}
`$3
| Export | Description |
|--------|-------------|
|
createSignHandler(options) | Creates a POST route handler |
| createSignAction(options) | Creates a server action factory |#### SignHandlerOptions
`typescript
interface SignHandlerOptions {
workspaceId: string;
workspaceSecret: string;
baseUrl?: string;
authorize?: (request: Request) => Promise | boolean;
getUser?: (request: Request) => Promise | FdbackUser | null;
}
`#### SignActionOptions
`typescript
interface SignActionOptions {
workspaceId: string;
workspaceSecret: string;
baseUrl?: string;
}
`---
CDN Usage
For non-bundled usage, include via CDN:
`html
`---
Complete Next.js Example
$3
`env
FDBACK_WORKSPACE_ID=your-workspace-id
FDBACK_WORKSPACE_SECRET=your-workspace-secret
`$3
`typescript
// app/api/fdback/sign/route.ts
import { createSignHandler } from "@fdback.io/sdk/next";
import { auth } from "@/server/auth";export const POST = createSignHandler({
workspaceId: process.env.FDBACK_WORKSPACE_ID!,
workspaceSecret: process.env.FDBACK_WORKSPACE_SECRET!,
authorize: async () => !!(await auth())?.user,
getUser: async () => {
const session = await auth();
if (!session?.user?.email) return null;
return {
email: session.user.email,
name: session.user.name ?? undefined,
};
},
});
`$3
`tsx
// app/layout.tsx
import { FdbackProvider } from "@fdback.io/sdk/react";export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
workspaceId={process.env.NEXT_PUBLIC_FDBACK_WORKSPACE_ID!}
signEndpoint="/api/fdback/sign"
>
{children}
);
}
`$3
`tsx
// components/feedback-button.tsx
"use client";
import { FeedbackButton } from "@fdback.io/sdk/react";
import { useSession } from "next-auth/react";export function AppFeedbackButton() {
const { data: session } = useSession();
if (!session?.user?.email) return null;
return (
user={{
email: session.user.email,
name: session.user.name ?? undefined,
avatar: session.user.image ?? undefined,
}}
className="fixed bottom-4 right-4 rounded-full bg-primary px-4 py-2 text-white"
>
Feedback
);
}
`---
Embedded Widget
The SDK includes an embeddable widget that can be integrated directly into your application. The widget provides a floating button and panel for collecting feedback.
$3
`html
`$3
For identified users, provide a
signEndpoint that authenticates users via HMAC-signed data:`html
`The
signEndpoint should return signed user data from your server. See Next.js Integration for creating sign endpoints.$3
`typescript
interface WidgetConfig {
workspaceId: string; // Required
mode?: "simple" | "full"; // "simple" = feedback only, "full" = tabs
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
tabs?: ("feedback" | "roadmap" | "changelog")[];
primaryColor?: string; // Hex color for theming
theme?: "auto" | "dark" | "light"; // Theme mode (default: "auto")
width?: number; // Widget width in pixels (default: 440, min: 320, max: 600)
height?: number; // Widget height in pixels (default: 680, min: 400, max: 900)
signEndpoint?: string; // URL for HMAC-signed authentication
onOpen?: () => void;
onClose?: () => void;
onFeedbackSubmitted?: (feedback: { id: string }) => void;
onTabChange?: (tab: string) => void;
onAuthenticated?: (user: { id: string; email: string; name?: string }) => void;
}
`$3
`typescript
const widget = FdbackSDK.widget.init(config);widget.open(); // Open the widget
widget.close(); // Close the widget
widget.toggle(); // Toggle open/closed
widget.setTab("roadmap"); // Switch tab (full mode)
widget.setUser({ email: "...", name: "..." }); // Update user data
widget.destroy(); // Remove widget from page
widget.isOpen; // Current state (boolean)
`$3
When
signEndpoint is configured:1. Widget initializes and calls your
signEndpoint with cookies
2. Your server reads the user session and returns HMAC-signed data
3. Widget sends signed data to fdback.io for verification
4. User is authenticated and feedback is attributed to them`
┌─────────────────────────────────────────────────────────────┐
│ YOUR APP (acme.com) │
│ 1. widget.init({ signEndpoint: '/api/fdback/sign' }) │
│ 2. SDK calls signEndpoint (with cookies for session) │
│ 3. Server returns HMAC-signed user data │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ FDBACK WIDGET (fdback.io) │
│ 4. Validates signature with workspace secret │
│ 5. Creates/updates user, sets session cookie │
│ 6. User is now authenticated │
└─────────────────────────────────────────────────────────────┘
`---
Troubleshooting
$3
The signed data expires after 5 minutes. Generate fresh data before opening.
$3
You're initializing
Fdback with a secret in client-side code. Remove the secret and use a backend API to sign requests.$3
You're importing
@fdback.io/sdk/next` in client code. These helpers are server-only. Create a server action or API route instead.---
MIT