Crash reports
File the crash your error screen shows as a bug in Signal, with one press.
"use client";
import { reportError, useCanReportError } from "@mediansh/widget";
import { useState } from "react";
export default function ErrorScreen({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
const canReport = useCanReportError();
const [note, setNote] = useState<string | null>(null);
async function report() {
const outcome = await reportError(error);
setNote(outcome.reported ? "Reported." : outcome.reason);
}
return (
<div>
<p>Something went wrong.</p>
<button onClick={reset}>Try again</button>
{canReport && note === null && (
<button onClick={report}>Report error</button>
)}
{note && <p>{note}</p>}
</div>
);
}reportError files the error as a bug in Signal, with the page and a screenshot. With diagnostics on, the browser's recent errors come too. It never throws. medianSupport.reportError and useMedianSupport().reportError are the same function.
Requirements
- A key.
MedianSupport,MedianSupportModal,MedianFeedback,MedianContactFormanduseMedianContacteach record their key as they mount.reportErroruses the last one recorded on this page load. PassapiKeywhen none has mounted. - Track bugs and suggestions turned on under Agent → Behavior. With it off, nothing is filed.
- A browser. On the server it returns a reason and sends nothing.
The key is held in memory, so it outlives an error boundary that replaced the tree the widget was in. A full page load forgets it.
In global-error.tsx, pass apiKey. A crash on the first page load can happen before any widget mounts.
const outcome = await reportError(error, {
apiKey: process.env.NEXT_PUBLIC_MEDIAN_PUBLIC_KEY,
});NEXT_PUBLIC_MEDIAN_PUBLIC_KEY holds the median_pk_ id from publicKeyFromMedianKey(MEDIAN_KEY). The id is safe in client code.
Options
| Name | Type | Description |
|---|---|---|
apiKey | string | The median_pk_ id. Defaults to the key of the last Median component that mounted. |
user | MedianUser | Who reported it. Defaults to the user of the last Median component that mounted. A signed id and hash tie the report to the person. |
route | string | Your router's name for the page, such as /orders/[id]. Leads the title. Cut at 200 characters. Default: window.location.pathname. |
componentStack | string | React's component stack, when your boundary has one. Default: error.componentStack. |
state | Record<string, string | number | boolean> | Facts for the report, placed before appState and winning any clash of keys. The same limits as appState. |
attach | Record<string, unknown> | Blocks to attach, as { label: value }. Added after anything queued with attach(). The same limits as attach(). |
telemetry | boolean | false leaves out time zone, language, browser, OS, device, screen, and the page title and referrer. The page URL still comes with the diagnostics snapshot. Default: true. |
screenshot | boolean | false files the report without a picture of the screen. Default: true. |
Limits for state and attach are on Context and diagnostics.
Outcome
type MedianErrorReportOutcome =
| { reported: true; joined: boolean }
| { reported: false; reason: string };joined is true when the report joined a bug that was already filed.
reason | When |
|---|---|
Median has no API key on this page, so the crash could not be reported. Mount <MedianSupport> or pass apiKey to reportError. | No Median component has mounted on this page load, and no apiKey was passed. Also printed to the console |
reportError only runs in a browser. | Called on the server |
Error reporting is currently unavailable. Try again later. | Track bugs and suggestions is off |
The report could not be sent. Check the connection and try again. | The request failed, for example offline, rate limited, or a revoked key. The console has the cause |
A report that does not land puts queued attach() entries back, together with the entries from the attach option. A second press carries them, and so does the next message, note or contact form send.
What lands on the report
| On the report | Value |
|---|---|
| Kind and priority | Bug, High |
| Title | Crash on /orders/[id]: TypeError: Cannot read properties of undefined. Without a route, Crash: TypeError: ... |
| Write-up | The page stopped working on the route and somebody reported it from the error screen, with Error and Digest lines |
| Quote | Reported from the error screen: followed by the error |
| Crash trace | error.stack |
| Component stack | componentStack |
| Route | The route option. Left out when the page URL already ends with it, which is the case for the default |
| Digest | error.digest from a Next.js server error |
| Page and Viewport | The page URL without its query string, and the window size |
| App state rows | Route, Digest and state, then appState from the last Median component given one |
| Browser logs | Recent errors, when a Median component on the page had diagnostics on |
| Screenshot | The error screen. Median panels are left out |
| Your blocks | Entries queued with attach(), then the attach option |
| Reporter | The visitor. Their browser and device go on their customer profile |
- The title leaves out the error's name when it is
ErrororException, or when the message already starts with it. - The message is cut at 300 characters. An error with no message is Unnamed error.
- Stacks are cut at 4,000 characters. The name and the digest are cut at 60.
- Route, Digest and
statecome first among the 20 app state entries, so a fullappStateloses its last entries rather than the crash's. - The report is filed at once. No model reads it first.
- A new bug is then checked against the list like any other, may be merged into a matching one, and goes to your connected issue tracker. See Signal.
Joining
- A report joins a bug with the same title, unless that bug was declined. Case and punctuation are ignored. Only the 60 most recent bugs that were not declined are compared.
- The same error on the same route gives the same title, so many people hitting one crash make one bug with one reporter row each.
- A visitor who reports the same crash again updates their row.
useCanReportError
const canReport = useCanReportError();| Returns | When |
|---|---|
false | On the server and during hydration |
false | No Median component with a key has mounted on this page load. With identity only, until the route answers |
true | A Median component has mounted with a key |
- It turns
trueby itself when a component mounts. You do not need to re-render. - It does not check Track bugs and suggestions. A press can still return the unavailable reason.
- It ignores the
apiKeyoption. If you always passapiKey, show the button without it.
Crash reports share the widget's rate limit for sends. See Conversations.