Skip to content

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

bash
npm install @feedvalue/react
# or
pnpm add @feedvalue/react
# or
yarn add @feedvalue/react

Basic Usage

Wrap your app with FeedValueProvider and use the useFeedValue hook:

tsx
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

PropTypeDefaultDescription
widgetIdstringrequiredYour widget ID from the dashboard
apiBaseUrlstring-Custom API URL (for self-hosted)
configobject-Configuration overrides
headlessbooleanfalseDisable default UI rendering
onReadyfunction-Called when widget is ready
onOpenfunction-Called when modal opens
onClosefunction-Called when modal closes
onSubmitfunction-Called after feedback submitted
onErrorfunction-Called when an error occurs

Hook Return Values

The useFeedValue hook returns:

tsx
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:

tsx
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.

tsx
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:

tsx
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 CaseSolution
Collect user's name/email in the formCustom Fields - define in dashboard, user fills in
Attach logged-in user's info automaticallyUser Identification - use identify(), hidden from UI
Let user select feedback categoryCustom Fields - define dropdown in dashboard
Track internal account IDsUser Identification - use setData(), hidden from UI

Setting Up Custom Fields

  1. Go to your widget settings in the FeedValue dashboard
  2. Add custom fields with types: text, email, or emoji
  3. Configure labels, placeholders, and whether fields are required
  4. 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:

tsx
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:

tsx
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

FeatureDefault ModeHeadless Mode
Trigger buttonDashboard-styledYou build it
ModalDashboard-styledYou build it
API methodsAvailableAvailable
User trackingAvailableAvailable
EventsAvailableAvailable
Dashboard configFetchedFetched

TIP

The React package automatically handles cleanup on unmount and prevents memory leaks. It uses useSyncExternalStore for React 18+ concurrent mode compatibility.

Built with care by SarverEnterprises