Shipeasy
Bugs & Requests

Case studies

Bug & feature request workflows — from "report bug" button to Linear sync.

Add a 'Report bug' button

The smallest possible integration: a draggable nub on every page that captures "something is wrong here" in two clicks. No modal, no form schema, no triage UI to build — @shipeasy/devtools mounts the overlay, lets the user file a bug or feature request with optional screenshot + console-log + repro-steps capture, and posts it through the admin endpoints with a short-lived browser-scoped admin token minted at /devtools-auth.

What this gets you:

  • Page URL, browser family, viewport — auto-captured.
  • The user's free-text answer to "what's broken?" and the reproduction steps prompt.
  • Screenshot of the page at the moment they filed it (configurable).
  • Console logs + the last few breadcrumbs leading up to the report.
  • The signed-in identity if you've called flags.identify({user_id}).
// app/layout.tsx — drop the devtools overlay once, anywhere on the page
"use client";
import { useEffect } from "react";
import { init as initDevtools } from "@shipeasy/devtools";

export function DevtoolsNub() {
  useEffect(() => initDevtools(), []);
  return null;
}

Users press Shift+Alt+B for a bug or Shift+Alt+R for a feature request. The nub also has a draggable click target for users who don't know the hotkey. The first time a user files a report from a given origin, a popup prompts them to approve the devtools session (the project-scoped admin token is bound to the popup's signed-in user) — a one-time click; subsequent reports go through silently for the session lifetime.

Stack-rank feature requests by demand

You're tired of hearing the same five feature requests in support and want to stack-rank them by actual demand instead of by who's loudest. Feature requests filed via the devtools nub (same Shift+Alt+R hotkey as the bug report flow above) land in Feedback → Feature requests with the requester's identity, page URL, free-text use case, and any attached screenshots.

The dashboard de-dupes by title-similarity + page so a flurry of "dark mode!" requests collapses into one card with a vote tally and the list of every requester. Sort the column by request count to see what's actually load-bearing demand vs the one squeaky user.

A public, vote-on-able roadmap surface — where signed-out visitors can up-vote requests on a shareable page — is on the roadmap but not shipped today. For now use the dashboard's stack-ranked view as the internal source of truth and pull the top items into your normal quarterly planning.

Forward triaged bugs to GitHub

You triage in Shipeasy (because that's where the dedup'd reports already live) but you ship in your tracker (GitHub Issues, for most teams). Don't make humans copy-paste; the GitHub connector does the transition for you.

Open Feedback → Connectors → Add connector → GitHub, point it at the target repo, and pick bug.created (and / or bug.status.changed) as the events. The connector opens a GitHub issue with the bug title, reproduction steps, page URL, and reporter — and writes the issue URL back onto the bug card so triagers can jump to GitHub straight from the Feedback tab.

For Linear / Jira / PagerDuty: the connector catalog doesn't ship them today. The GitHub flow is the workaround until those land — file via GitHub Issues, mirror into Linear from there. Or shell out from a Claude trigger connector that calls Linear's API on each new bug event; the trigger fires claude with a prompt that has the bug context baked in.

The pattern that closes the loop in either case:

  • The connector writes the external URL back onto the bug card's external_link field. Triagers jump from Feedback → GitHub / Linear with a single click.
  • Dedup before forwarding. Shipeasy collapses bug reports by page + repro fingerprint before the connector sees them, so a flurry of users hitting the same bug doesn't create N GitHub issues.

Auto-attach session replay

A title and a URL is rarely enough to reproduce a bug. The user clicked something, then something broke, and the something is what you actually need to see. If you already run a session-replay tool (Sentry, FullStory, LogRocket, Highlight), the integration is two lines.

The devtools overlay's bug-report form has a free-text Notes field and an optional Screenshots capture. Until first-class replay attachments ship, the practical pattern is to paste the replay URL into the Notes field. The dashboard renders Notes as Markdown, so a plain https://sentry.io/replays/<id> link is clickable from the bug card.

For a one-click flow, drop a Report bug shortcut on your page header that pre-fills the URL alongside the devtools nub trigger:

"use client";
import { useEffect } from "react";
import { init as initDevtools } from "@shipeasy/devtools";
import * as Sentry from "@sentry/nextjs";

export function ReportBugWithReplay() {
  useEffect(() => initDevtools(), []);
  return (
    <button
      onClick={() => {
        const replayId = Sentry.getReplay()?.getReplayId();
        if (replayId) {
          // Stash on `window` so the user can paste it into the
          // devtools Notes field when the bug form opens.
          (window as unknown as { __se_replay?: string }).__se_replay =
            `Replay: https://sentry.io/replays/${replayId}`;
        }
        // Fire the same hotkey the nub binds.
        window.dispatchEvent(
          new KeyboardEvent("keydown", {
            key: "B",
            shiftKey: true,
            altKey: true,
          }),
        );
      }}
    >
      Report bug (with replay)
    </button>
  );
}

A few decisions:

  • Don't store the replay payload in Shipeasy. Store the URL. Replays are large, they have their own retention story, and you don't want two systems holding the same blob.
  • Sample-aware. Most replay tools sample (e.g. 10% of sessions). If a user hits the bug button and getReplayId() is null, the bug still files — just without a replay link. Drop the link, not the report.

On this page