Skip to content
Feeblo Docs
Esc
navigateopen⌘Jpreview
On this page

Triggers and events

Open the Feeblo widget with markup attributes or code, then subscribe to widget events in vanilla JS or React.

The widget opens two ways: a reader clicks an element carrying the data-feeblo-feedback attribute, or your code calls open(). While the widget runs, it emits five events your JavaScript can subscribe to.

Opening the widget from markup

Add data-feeblo-feedback to any element:

<button data-feeblo-feedback>Give feedback</button>

The SDK finds these elements at init and rescans the page every second, so buttons you render later pick up the trigger shortly after they mount. Each element binds once. A click calls preventDefault() and stopPropagation() first, which means an anchor trigger won’t navigate away.

Trigger metadata

Any other attribute starting with data-feeblo- travels with the click:

<button data-feeblo-feedback data-feeblo-board="roadmap" data-feeblo-plan="enterprise">
  Give feedback
</button>

data-feeblo-board opens that board instead of the default one (board selection needs feedback as your landing module). Every other attribute becomes a metadata key: drop the prefix and camelCase the rest, so data-feeblo-plan arrives as plan. The widget records this metadata on posts submitted while it’s set, and the feedbackSubmitted event hands it back to you. That answers “which button sent us here?” after the fact.

Opening the widget from code

init returns a chainable handle with open, close, and openModule:

import { Feeblo } from "@feeblo/sdk";

const widget = Feeblo.init("org_123");

widget.open();
widget.close();
widget.openModule("updates");
widget.isOpen(); // true or false

openModule takes "feedback" or "updates" and only switches to modules the current config enables. In single-module modes there’s nothing to switch to; in hub mode it jumps between the modules you listed. Because methods chain, widget.setBoard("roadmap").open() works as one statement. The same methods also hang off the static Feeblo object and act on the current instance.

useFeeblo exposes open, close, and openModule, plus a reactive isOpen you can render from:

import { useFeeblo } from "@feeblo/sdk-react";

function FeedbackButton() {
  const feeblo = useFeeblo();

  return (
    <button onClick={() => feeblo.openModule("feedback")}>
      {feeblo.isOpen ? "Close feedback" : "Give feedback"}
    </button>
  );
}

The five events

Event names are exact strings:

Event Fires when event.detail.data
widgetReady The widget iframe finished loading undefined
widgetOpened The widget opened { module }, when the widget reported it
widgetClosed The widget closed undefined
identityChanged An identify call reached the widget The identity object, minus the token
feedbackSubmitted The reader submitted a post A { boardId, boardName, title, metadata? } object

Under the hood, each event is a CustomEvent dispatched on window whose detail carries { data, type, namespace }, with namespace always "feeblo". You can listen with window.addEventListener directly; the helpers below add typing and cleanup.

Listening in vanilla JS

Feeblo.on subscribes and returns an unsubscribe function:

const off = Feeblo.on("feedbackSubmitted", (event) => {
  const post = event.detail.data;
  analytics.track("feedback_submitted", {
    board: post?.boardName,
    title: post?.title,
  });
});

// Stop listening when you no longer care.
off();

TypeScript narrows event.detail.data from the event name, so post is typed without any casting on your side.

If you prefer symmetric add/remove, Feeblo.off takes the same arguments:

function onReady() {
  enableFeedbackButton();
}

Feeblo.on("widgetReady", onReady);
Feeblo.off("widgetReady", onReady);

Pass "*" as the event name to observe everything at once. event.detail.type tells you which event fired:

Feeblo.on("*", (event) => {
  console.log("[feeblo]", event.detail.type, event.detail.data);
});

Listening in React

The useFeebloEvent hook wraps the same subscription for component lifetimes:

import { useFeebloEvent } from "@feeblo/sdk-react";

export function FeedbackAnalytics() {
  useFeebloEvent("feedbackSubmitted", (event) => {
    analytics.track("feedback_submitted", event.detail.data);
  });

  return null;
}

How it behaves:

  • The subscription lasts as long as the component does; unmounting removes it.
  • Pass "*" to observe every event.
  • Write the handler inline. The hook reads it through a ref, so a fresh closure on each render never triggers a resubscribe, and every invocation reaches the latest handler.
  • The hook talks to the core SDK directly rather than the provider context, so it works outside <FeebloProvider> too.

Widget events vs webhooks

Widget events live in the browser of whoever typed the feedback. Close the tab and they’re gone, and your server hears nothing about posts other people submit. To react on the backend, subscribe to webhooks instead: Feeblo delivers feedback.post.created and feedback.post.status_changed over HTTPS regardless of who is online.

Next steps

Was this page helpful?