Errors · Guides
Catch browser errors
Uncaught errors, rejected promises and React's own, with the clicks and page changes before them, and who it was from a token your server signs. Optional, and about ten lines.
Before you start
- The Laravel SDK is installed and
MYBEAST_ERRORS_PUBLIC_KEYandMYBEAST_ERRORS_SIGNING_KEYare in.env(the quickstart, steps 2 and 3). Without the signing key, every browser error is a guest's. - Your app's origin, such as
https://app.example.com, is listed in Errors' Settings → Browser origins. Errors takes browser errors only from a listed origin.
1. Hand the page its config
The Laravel SDK makes what the page needs: where to send, the public key, the environment and the release and, for someone signed in, a token saying who they are. With Inertia, share it with every page:
use MyBeast\Errors\Laravel\Facades\Errors;
public function share(Request $request): array
{
$user = $request->user();
return [
...parent::share($request),
// Ids only: the person, then their account (a team), if your app has one.
'mybeastErrors' => $user
? Errors::clientConfig($user->id, $user->team_id)
: Errors::guestConfig(),
];
}Without Inertia, put it in your layout as JSON:
@use('MyBeast\Errors\Laravel\Facades\Errors')
<script type="application/json" id="mybeast-errors">
@json(auth()->check() ? Errors::clientConfig(auth()->id()) : Errors::guestConfig())
</script>Give it a name of its own, as mybeastErrors here: Inertia already shares an errors prop, and Blade an $errors variable, for validation errors.
Both methods take ids only: clientConfig() with something that isn't an id, an e-mail say, gives the guest's config and says so once in your log. Both return null while the SDK is off, and the page then sends nothing.
2. Install the browser SDK
npm install @mybeast/errorsThen, before your app renders:
import { init, createErrorReporter } from '@mybeast/errors';
// Inertia: props.initialPage.props.mybeastErrors in createInertiaApp's setup().
// Blade: the JSON in the layout.
const config = JSON.parse(document.getElementById('mybeast-errors').textContent);
createErrorReporter(init(config)).install();install() listens on the window for uncaught errors and unhandled rejections, and keeps a trail of the last 20 clicks and page changes to send with the next error. Each error is sent the moment it happens, in a request that survives the page crashing or closing.
3. React and routers
React 19 catches errors in rendering before the window sees them. Hand createRoot the reporter's options and both kinds are sent: what an error boundary caught, and what unmounted the app.
import { createRoot } from 'react-dom/client';
createRoot(element, reporter.reactRootOptions({ onUncaught: () => showCrashScreen() })).render(<App />);A router that changes pages with history.pushState fires no event, so tell the reporter: reporter.navigated(url). Only the path is kept. Back and forward are seen without it.
What leaves the page
The SDK fails closed: what it can't tell is safe, it hides.
- A pressed button's label leaves as stars,
*** ****forPay 1240, unlessmaskLabelkeeps it. Inside an element markeddata-mybeast-privateit is always starred. - A link leaves only its path, never its query or fragment. So does the page's own address.
- Who it was comes from the token only. A token made for another project is ignored, and the page reports as a guest.
- At most 10 reports per page load, the same error once, and known noise (browser extensions,
ResizeObserverwarnings, cross-originScript error.) is dropped.
A fuller setup, with the page's name, your own button words kept and a crash screen:
import { init, createErrorReporter, star } from '@mybeast/errors';
const client = init(config); // null when the config is missing or malformed
const reporter = createErrorReporter(client, {
currentPage: () => page.component, // what is on screen, e.g. 'Invoices/Show'
maskLabel: (label) => (ourWords.has(label) ? label : star(label)),
onReported: ({ reference }) => showCrashScreen(reference),
});
reporter.install();Every option is in the browser SDK reference.
A page left open
A token lives 30 minutes. Two minutes before it runs out, and when the tab comes back into view, the page asks your app for a fresh one at /mybeast/errors/token, a route the Laravel SDK adds in the web group: the person's own session decides who. Once nobody is signed in, it answers 204 and the page goes on as a guest.
Readable stacks need source maps
Check it works
Open a page of your app, then in the browser's console:
setTimeout(() => { throw new Error('My first browser error'); });The issue appears in Errors within seconds, as a browser error, with the clicks before it. Nothing? Browser errors don't arrive.