React Integration
React components and hooks for fdback.io
npm install @fdback.io/sdk reactSetup Provider
Wrap your app with the FdbackProvider:
// app/providers.tsx or app/layout.tsx
import { FdbackProvider } from "@fdback.io/sdk/react";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<FdbackProvider
workspaceId="your-workspace-id"
signEndpoint="/api/fdback/sign"
>
{children}
</FdbackProvider>
);
}FeedbackButton Component
The easiest way to add feedback - a ready-to-use button:
"use client";
import { FeedbackButton } from "@fdback.io/sdk/react";
export function Header() {
const user = useCurrentUser(); // Your auth hook
return (
<FeedbackButton
user={{ email: user.email, name: user.name }}
className="btn btn-primary"
onOpen={(win) => console.log("Opened!", win)}
onError={(err) => console.error(err)}
>
Give Feedback
</FeedbackButton>
);
}FdbackWidget Component
Use FdbackWidget when you want the embedded floating widget in a React or Next.js app:
"use client";
import { FdbackWidget } from "@fdback.io/sdk/react";
export function AppWidget() {
return (
<FdbackWidget
workspaceId="your-workspace-id"
signEndpoint="/api/fdback/sign"
mode="full"
tabs={["feedback", "roadmap", "changelog"]}
showBoardLink={true}
theme="auto"
colors={{
primary: "#6366f1",
light: {
background: "#ffffff",
foreground: "#111827",
},
dark: {
background: "#111827",
foreground: "#f9fafb",
},
}}
/>
);
}Set showLauncher={false} if you want to render your own trigger and control the widget with open / onOpenChange.
Set showBoardLink={true} to show an Open board link inside the widget. If the widget is authenticated through signEndpoint, the link uses the same signed login flow as the Core SDK so the user is logged in on the board too.
colors accepts hex values and valid CSS color strings such as oklch(...). When using oklch(...) for primary, set primaryForeground explicitly because automatic foreground contrast is only derived from hex colors.
useFdbackLogin Hook
For custom implementations with loading and error states:
"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 (
<button onClick={handleClick} disabled={isLoading}>
{isLoading ? "Opening..." : "Feedback"}
</button>
);
}useFdback Hook
Access the Fdback context directly:
"use client";
import { useFdback } from "@fdback.io/sdk/react";
export function FeedbackStatus() {
const { isOpen, getBoardUrl, workspaceId } = useFdback();
return (
<div>
<p>Workspace: {workspaceId}</p>
<p>Board URL: {getBoardUrl()}</p>
<p>Popup open: {isOpen ? "Yes" : "No"}</p>
</div>
);
}API Reference
| Export | Type | Description |
|---|---|---|
FdbackProvider | Component | Context provider with workspace config |
FeedbackButton | Component | Ready-to-use button with built-in signing |
FdbackWidget | Component | Embedded floating widget |
useFdback | Hook | Access context (client, isOpen, etc.) |
useFdbackLogin | Hook | Login + open with loading/error state |
useWidget | Hook | Programmatic control for the embedded widget |
FdbackProvider Props
interface FdbackProviderProps {
workspaceId: string; // Your workspace ID
signEndpoint?: string; // API endpoint for signing (e.g., "/api/fdback/sign")
baseUrl?: string; // Custom base URL
children: React.ReactNode;
}FeedbackButton Props
interface FeedbackButtonProps {
user: FdbackUser; // User data to sign
mode?: "popup" | "tab" | "redirect"; // How to open (default: "popup")
onOpen?: (window: Window | null) => void;
onError?: (error: Error) => void;
className?: string;
children?: React.ReactNode;
}FdbackWidget Props
interface FdbackWidgetProps {
workspaceId: string;
baseUrl?: string;
mode?: "simple" | "full";
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
tabs?: ("feedback" | "roadmap" | "changelog")[];
showBoardLink?: boolean;
primaryColor?: string;
colors?: WidgetThemeColors;
launcherIcon?: string;
theme?: "auto" | "dark" | "light";
width?: number;
height?: number;
signEndpoint?: string;
user?: { email: string; name?: string };
open?: boolean;
onOpenChange?: (open: boolean) => void;
showLauncher?: boolean;
onFeedbackSubmitted?: (feedback: { id: string }) => void;
onTabChange?: (tab: string) => void;
onAuthenticated?: (user: { id: string; email: string; name?: string | null }) => void;
}
interface WidgetThemeColors extends WidgetThemeColorTokens {
light?: WidgetThemeColorTokens;
dark?: WidgetThemeColorTokens;
}
interface WidgetThemeColorTokens {
primary?: string;
primaryForeground?: string;
background?: string;
foreground?: string;
card?: string;
cardForeground?: string;
popover?: string;
popoverForeground?: string;
muted?: string;
mutedForeground?: string;
accent?: string;
accentForeground?: string;
border?: string;
input?: string;
ring?: string;
}