React Integration
Add FeedValue to your React application with full TypeScript support.
Coming Soon
The @feedvalue/react package is currently in development. Check back soon for availability on npm. In the meantime, use the script tag method.
Installation
npm install @feedvalue/react
# or
pnpm add @feedvalue/react
# or
yarn add @feedvalue/reactBasic Usage
Wrap your app with FeedValueProvider and use the useFeedValue hook:
import { FeedValueProvider, useFeedValue } from '@feedvalue/react';
function App() {
return (
<FeedValueProvider widgetId="your-widget-id">
<YourApp />
</FeedValueProvider>
);
}
function FeedbackButton() {
const { open, isReady } = useFeedValue();
return (
<button onClick={open} disabled={!isReady}>
Give Feedback
</button>
);
}Provider Props
| Prop | Type | Default | Description |
|---|---|---|---|
widgetId | string | required | Your widget ID from the dashboard |
apiBaseUrl | string | - | Custom API URL (for self-hosted) |
config | object | - | Configuration overrides |
headless | boolean | false | Disable default UI rendering |
onReady | function | - | Called when widget is ready |
onOpen | function | - | Called when modal opens |
onClose | function | - | Called when modal closes |
onSubmit | function | - | Called after feedback submitted |
onError | function | - | Called when an error occurs |
Hook Return Values
The useFeedValue hook returns:
const {
// State
isReady, // Widget is ready (config loaded)
isOpen, // Modal is currently open
isVisible, // Trigger button is visible
isSubmitting, // Submission in progress
isHeadless, // Running in headless mode
error, // Current error (if any)
instance, // FeedValue instance (advanced usage)
// Actions
open, // Open the feedback modal
close, // Close the feedback modal
toggle, // Toggle the modal open/closed
show, // Show the trigger button
hide, // Hide the trigger button
submit, // Submit feedback programmatically
identify, // Identify the current user
setData, // Set user data
reset, // Reset user data
} = useFeedValue();Headless Mode
For complete UI control, use headless mode. This disables the default trigger button and modal, letting you build your own:
import { FeedValueProvider, useFeedValue } from '@feedvalue/react';
function App() {
return (
<FeedValueProvider widgetId="your-widget-id" headless>
<CustomFeedbackUI />
</FeedValueProvider>
);
}
function CustomFeedbackUI() {
const { isReady, isOpen, open, close, submit, isSubmitting } = useFeedValue();
const [message, setMessage] = useState('');
const handleSubmit = async () => {
await submit({ message });
setMessage('');
close();
};
return (
<>
<button onClick={open} disabled={!isReady}>
Feedback
</button>
{isOpen && (
<div className="my-modal">
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<button onClick={handleSubmit} disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Submit'}
</button>
<button onClick={close}>Cancel</button>
</div>
)}
</>
);
}User Identification
Attach user context to feedback submissions. This data is not shown in the widget UI but is stored with the submission and visible in your FeedValue dashboard.
function UserProfile({ user }) {
const { identify, setData } = useFeedValue();
useEffect(() => {
// Identify user with ID and traits
identify(user.id, {
email: user.email,
name: user.name,
plan: user.plan,
company: user.company,
});
// Or set additional data
setData({
internalAccountId: 'acc_12345',
source: 'web-app',
});
}, [user]);
return <div>{user.name}</div>;
}User Data vs Custom Fields
- User data (
identify/setData): Hidden from users, automatically attached to submissions. Use for internal context like user IDs, subscription plans, etc. - Custom fields (
customFieldValues): Shown as form inputs in the widget. Users fill these in themselves. Must be defined in widget configuration first.
Programmatic Submission
Submit feedback without the UI:
function QuickFeedback() {
const { submit, isReady } = useFeedValue();
const sendFeedback = async (sentiment: 'satisfied' | 'disappointed') => {
await submit({
message: 'Quick feedback',
sentiment,
});
};
return (
<div>
<button onClick={() => sendFeedback('satisfied')} disabled={!isReady}>
Happy
</button>
<button onClick={() => sendFeedback('disappointed')} disabled={!isReady}>
Not Happy
</button>
</div>
);
}Custom Fields (User-Facing Form Inputs)
Custom fields are form inputs shown in the widget UI that users fill in themselves. They must be defined in your widget configuration on the FeedValue dashboard.
When to Use Custom Fields vs User Identification
| Use Case | Solution |
|---|---|
| Collect user's name/email in the form | Custom Fields - define in dashboard, user fills in |
| Attach logged-in user's info automatically | User Identification - use identify(), hidden from UI |
| Let user select feedback category | Custom Fields - define dropdown in dashboard |
| Track internal account IDs | User Identification - use setData(), hidden from UI |
Setting Up Custom Fields
- Go to your widget settings in the FeedValue dashboard
- Add custom fields with types:
text,email, oremoji - Configure labels, placeholders, and whether fields are required
- The fields will automatically appear in the widget modal
Headless Mode: Submitting Custom Field Values
When using headless mode with your own UI, use customFieldValues to submit responses:
function DetailedFeedback() {
const { submit, isReady } = useFeedValue();
const [name, setName] = useState('');
const [category, setCategory] = useState('');
const handleSubmit = async () => {
await submit({
message: 'Detailed feedback message',
customFieldValues: {
// Field IDs must match those defined in your widget configuration
name: name,
category: category,
},
});
};
return (
<form onSubmit={handleSubmit}>
<input value={name} onChange={(e) => setName(e.target.value)} placeholder="Your name" />
<select value={category} onChange={(e) => setCategory(e.target.value)}>
<option value="bug">Bug Report</option>
<option value="feature">Feature Request</option>
<option value="other">Other</option>
</select>
<button type="submit" disabled={!isReady}>Submit</button>
</form>
);
}Custom Fields Must Be Defined First
The field IDs in customFieldValues (e.g., name, category) should match field IDs defined in your widget configuration on the dashboard.
## Event Callbacks
Handle events at the provider level:
```tsx
<FeedValueProvider
widgetId="your-widget-id"
onReady={() => console.log('Widget ready')}
onOpen={() => analytics.track('feedback_opened')}
onClose={() => analytics.track('feedback_closed')}
onSubmit={(feedback) => {
console.log('Feedback:', feedback);
analytics.track('feedback_submitted', feedback);
}}
onError={(error) => {
console.error('FeedValue error:', error);
Sentry.captureException(error);
}}
>
<App />
</FeedValueProvider>TypeScript Support
Full TypeScript support is included:
import type {
FeedValueProviderProps,
FeedValueContextValue,
FeedbackData,
UserTraits,
} from '@feedvalue/react';
const handleSubmit = (feedback: FeedbackData) => {
console.log(feedback.message);
console.log(feedback.sentiment); // 'angry' | 'disappointed' | 'satisfied' | 'excited'
};
const traits: UserTraits = {
email: '[email protected]',
plan: 'pro',
};Comparison: Default vs Headless
| Feature | Default Mode | Headless Mode |
|---|---|---|
| Trigger button | Dashboard-styled | You build it |
| Modal | Dashboard-styled | You build it |
| API methods | Available | Available |
| User tracking | Available | Available |
| Events | Available | Available |
| Dashboard config | Fetched | Fetched |
TIP
The React package automatically handles cleanup on unmount and prevents memory leaks. It uses useSyncExternalStore for React 18+ concurrent mode compatibility.
