Your changelog, feedback, and surveys — inside your app.
One script tag drops a changelog, feedback, and survey launcher into any web app — isolated in its own Shadow DOM, themed to your brand, with an unread badge. Then call ProductLog.identify() to attribute every vote and post to a real person. Both free.
One script tag. No build step.
Paste a single <script> on the page you want the launcher on. It auto-initializes from the
data-* attributes after DOMContentLoaded — no bundler, no npm install required.
The whole widget lives in its own Shadow DOM, so its styles never leak into your app and your app’s CSS never breaks it.
- Single IIFE bundle, served content-hashed and cached aggressively
- Shadow-DOM isolation — zero style collisions either way
- Reads
data-project,data-color,data-position,data-tabs,data-theme - Talks only to ProductLog’s public APIs — your pages stay script-light
<!-- Drop this once, anywhere on the page --> <script src="https://productlog.dev/widget.js" data-project="your-project-slug" data-color="#6d28d9" data-position="bottom-right" data-tabs="changelog,feedback,survey" data-theme="auto"></script>
Three tabs, one unread badge.
The widget is a floating action button in the corner you choose. Open it for the latest releases, a feedback board, and the active survey — all reading from your public ProductLog.
Changelog tab
Your latest published entries, paginated, with a “view all” link to the full public page.
Feedback tab
Browse boards and posts, upvote, and submit a new post — all through the public feedback API, without leaving your app.
Survey tab
Surface your active survey and collect a response right in context — NPS, CSAT, or a custom question.
Unread badge
The launcher shows a count of changelog entries published since the visitor last opened it — capped at 99+. Opening the tab marks everything seen and clears the badge.
Themed to your brand
Set the accent color, corner, and the visible tabs. The FAB, badge, tabs, links, and buttons all follow your accent automatically.
Light, dark, or auto
Choose light, dark, or auto — auto follows the visitor’s OS prefers-color-scheme.
Tweak it from the script tag.
Every option is a data-* attribute — pick a corner, choose which tabs appear, set the accent, and lock the theme. Invalid values are simply ignored, so you can never break the embed with a typo.
Positions
Tabs (any subset)
Theme
// Prefer config over data-* attributes? Call init() yourself. window.ProductLog.init({ project: "your-project-slug", primaryColor: "#6d28d9", position: "bottom-left", tabs: ["changelog", "feedback"], theme: "auto" });
accentColor is an accepted alias for primaryColor. Omit tabs to show all three.
Know who’s actually voting.
Anonymous feedback is hard to act on. Call ProductLog.identify() and every vote, post, and survey response attaches to a real person — and you can enrich their profile with plan, MRR, and company.
From anonymous visitor to a profile you can act on.
Pass an externalId (or an email) and ProductLog attributes the current session to that person, upserting an EndUser profile. Layer on plan, mrr, and company to see which accounts are driving the most demand — so you prioritize revenue, not noise.
- Identity key is
externalId(orid), falling back toemail - Profiles carry
plan,mrr(in cents),company, and arbitrary attributes - Upserted by
externalIdthen email, andlast_seenis refreshed each call - Same free SDK that backs the widget — no extra entitlement
window.ProductLog.identify({ externalId: "host-user-id", email: "[email protected]", name: "Ada Lovelace", plan: "pro", // signed-only mrr: 4900, // signed-only (cents) company: "Acme" // signed-only }, signature); // HMAC computed on YOUR server
Revenue data your visitors can’t spoof.
Anything sensitive — plan, mrr, company, and custom attributes — is honored only with a valid signature your server computes. The signing key never reaches the browser, so a logged-in visitor can never POST themselves a $10k MRR.
HMAC over canonical JSON, verified server-side.
On your server, derive the project signing key, canonicalize the traits, and sign them. The browser sends the traits plus that signature; ProductLog re-derives the key, re-canonicalizes, and compares in constant time.
- Key =
HMAC-SHA256(APP_SECRET, "identity:" + projectId)— derived server-side, never shipped to the browser - Canonical form: recursive key-sort, then
JSON_UNESCAPED_SLASHES | UNICODE - Signature is a hex HMAC-SHA256, compared with a constant-time check
- Identify runs against a
PUBLIC_ACCESSendpoint — no API key in the browser
Valid signature
Every trait is trusted, including plan, mrr, and company. The profile is enriched in full.
No signature
Accepted, but downgraded: only id/externalId, email, and name are kept. The signed-only traits are dropped — an anonymous caller can never poison your revenue data.
Invalid signature
Rejected outright with a 401 — never a silent fallback. A tampered payload fails loudly.
Better with the rest of ProductLog.
The widget surfaces what you publish elsewhere; identify makes that activity legible.
Changelog
The widget’s Updates tab and unread badge are powered by your published changelog — every release shows up in-app the moment you ship it.
Learn moreFeedback
Votes and posts collected through the widget land on your boards — and once you identify the user, every one of them is tied to a real person and account.
Learn moreBranding
Set the accent the widget inherits, alongside your logo, colors, and custom domain — so the launcher looks like part of your product, not a bolt-on.
Learn morePut ProductLog inside your app in two minutes.
The widget and Identify SDK are free on every plan — including Free. Grab your script tag and embed it today.