Widget
The widget is your portal inside your own product: feedback, the roadmap and the changelog, in a frame on your pages.
What it is#
The widget is your portal's /widget page, made for a frame: a compact header with New post, your sections as
tabs, and your accent color. People search, post, vote, comment and follow there as on the portal, with the same
boards and moderation. It speaks each visitor's browser's language, as
the portal does, without the portal's language menu. A link that leaves the widget, such as
the one to the full portal, opens in a new tab.
Today you embed the widget as an iframe, where you want it and at the size you want. It shows no notifications and no account page: people find those on the portal, where they sign in on their own.
Settings#
Under Settings → Widget:
- Sections: Feedback, Roadmap and Changelog, as tabs in that order. Keep at least one. The roadmap shows only while the portal shows it, under Settings → General.
- An accent color of your own: the color of the widget's buttons. Without one, the widget takes the portal's accent color, from Settings → Branding.
- Side: bottom right or bottom left. It doesn't move the iframe, which sits where your page puts it.
- Allowed origins: the sites that may embed the widget, one per line, such as
https://app.example.com. At most 20.https://*.example.comallows the subdomains ofexample.com, but notexample.comitself. Use https, except forlocalhost. Left empty, any site may embed the widget.
With allowed origins, browsers refuse to show the widget on any other site, and the widget ignores messages from them. A change reaches the widget within 30 seconds.
Embed it#
Settings → Widget gives the snippet with your portal's address, such as:
<iframe src="https://acme.example.com/widget" title="Feedback" style="width: 400px; height: 640px; border: 0"></iframe>Put it where you want feedback in your product, and change the size to fit. Until your page signs the user in, people can read the widget. When they try to write, it asks them to sign in to your product, and offers to continue as a guest where the portal takes guests.
Sign your user in#
Your server signs an identity token for the user signed in to your product: Signing in your users shows how. Your page then hands the token to the frame:
-
When the frame can take a token, it posts
{ source: "swingback", type: "ready" }to your page. -
Your page posts the token to the frame, as Settings → Widget shows:
frame.contentWindow.postMessage({ source: "swingback", type: "identify", token }, "https://acme.example.com"); -
The frame answers
{ source: "swingback", type: "identified" }and shows your user signed in, or{ source: "swingback", type: "error", code }with the reason.
A page with the widget and its sign-in:
<script>
// Before the iframe, so the page hears "ready".
const portal = "https://acme.example.com";
window.addEventListener("message", async (event) => {
const frame = document.getElementById("feedback");
if (event.origin !== portal || event.source !== frame.contentWindow) return;
const message = event.data;
if (message?.source !== "swingback") return;
if (message.type === "ready") {
// An endpoint of your product that signs a token for the signed-in user.
const { token } = await fetch("/feedback-token").then((response) => response.json());
frame.contentWindow.postMessage({ source: "swingback", type: "identify", token }, portal);
} else if (message.type === "error") {
console.warn("Feedback widget:", message.code);
}
});
</script>
<iframe
id="feedback"
src="https://acme.example.com/widget"
title="Feedback"
style="width: 400px; height: 640px; border: 0"
></iframe>When your user signs out of your product, sign them out of the widget too. The frame answers
{ source: "swingback", type: "signed-out" }.
frame.contentWindow.postMessage({ source: "swingback", type: "logout" }, "https://acme.example.com");Errors#
code | Why |
|---|---|
invalid_identity_token | The token was refused: expired, signed with a revoked secret, or with a wrong claim. |
validation_failed | The token's email, name, avatar_url or companies aren't valid. |
end_user_conflict | The token's email belongs to another customer, with another id. |
rate_limited | More than 120 sign-ins from one address in 15 minutes. |
Other codes mean the service couldn't take the token at the time: try again later.
Good to know#
- The frame takes messages only from the page around it, and only from an allowed origin when you list some.
- A token works until it expires, so your page can send the same one again after a reload. Signing in the user the widget already has keeps their session, and brings their profile up to date. Anyone else gets a new session, and the previous one ends. Sign in once per page load.
- What someone wrote in the widget as a guest becomes theirs when your page signs them in.
- The widget keeps its session in a cookie of its own, for your site only. Being signed in to the widget doesn't sign anyone in to the portal, and the reverse.
