Headless in-app bug reports that arrive with the evidence attached.
Your UI, your endpoint, a few kilobytes.
Error trackers catch what throws. They cannot catch what merely looks wrong, and they never tell you what the person was doing when it did. “The save button does nothing” is not a report anyone can act on.
bugbottle collects the context at the moment someone notices — the page, the viewport, the recent console errors, the element they point at, optionally a picture of what they were looking at — and POSTs it as JSON to a route you already own.
What you get
- Headless. You render the form. This owns the state, the capture and the submit — not your markup.
-
Bring your own backend.
No dashboard, no hosted service. A report is a JSON body on a
fetch, received by a route handler you write, with the validation helpers shipped alongside. - Nothing in your bundle you did not ask for. Zero dependencies. The core is about 0.6 kB gzipped; with the element picker and the React hook, 3.4 kB; the optional ready-made panel, 6 kB.
-
Point at the element.
pickElement()resolves with a short CSS selector, the tag, the visible text, the position and the useful attributes — enough for a person or a coding agent to land on the right file. - Your language, your brand. Eight bundled locales, every string overridable, and a panel themed with a handful of CSS variables.
- Server helpers included. Every field a browser sends is checked before it reaches your database, because that is where the sharp edges are.
Three lines
Install it, start recording console errors, mount the ready-made panel.
npm install bugbottle
import { initConsoleBuffer } from "bugbottle";
import { mountBugbottle } from "bugbottle/ui";
initConsoleBuffer();
mountBugbottle({ endpoint: "/api/feedback" });
Prefer to build the form yourself? useBugReport from
bugbottle/react gives you the same core with none of the
markup. See the
README.
The panel, live on this page
The floating button in the corner is the real
bugbottle/ui panel. Open it, write a line, point at
something on this page, and press send.
There is no endpoint behind it: the demo passes its own
fetch, keeps the body that would have been posted, and
renders it below with toMarkdown from
bugbottle/server — the same function you would use for an
issue body or a morning digest. Nothing leaves your browser. Screenshots
are switched off here, because the demo has nowhere private to put one.
The payload, as Markdown
Please read this part
A screenshot of your application contains whatever the reporter could see. In a clinical system that can mean a patient photograph; in a payroll tool, a salary; in yours, perhaps somebody's inbox or a half-written message they had not sent yet.
Three things follow, and the library cannot do them for you:
- Put screenshots somewhere private. If your object storage bucket has a public read policy — many media buckets do — anything you write to it can be fetched by anyone holding the URL. Use a separate bucket with no public policy.
- Serve them back through an authenticated route. Never give a screenshot a public URL. Look the storage key up from the row rather than taking it from the request, so an id cannot be used to walk your bucket.
- Say so before the picture is taken. Put it in the form, next to the checkbox — not in a policy nobody opens.
Requiring people to be signed in is worth considering too. An anonymous screenshot is one nobody can be asked about later, and nobody can be told has been deleted. The full section in the README.