# my-bad
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![Github Actions][github-actions-src]][github-actions-href]
[![Codecov][codecov-src]][codecov-href]
> Beautiful dev-server error pages rendered to HTML, ANSI or JSON and live updated over SSE. ✨
`my-bad` turns an error into a plain JSON error report with sourcemapped frames, snippets, causes, component trace and request context.
You can then use `my-bad` to render that report as a full HTML page, a shadow-DOM overlay for an existing page, or ANSI for the terminal. A small SSE channel keeps browser-based pages live, meaning errors can be updated, the page can reload when the error is fixed, and additional warnings and server logs are surfaced as they occur.
The same report, rendered to the terminal with `renderAnsi`:
## Install
```sh
npm install my-bad
```
## Usage
```ts
import { createReport, fsLoader, renderAnsi, renderPage } from 'my-bad'
import { nuxtPreset } from 'my-bad/presets'
const report = await createReport(error, {
cwd: process.cwd(),
loaders: [fsLoader()],
presets: [nuxtPreset({ versions: { nuxt: '4.0.0' } })],
context: { event },
})
console.error(renderAnsi(report))
if (req.headers.accept?.includes('text/html')) {
res.end(renderPage(report, { cwd: process.cwd(), channel: '/__my-bad' }))
}
else {
res.end(JSON.stringify(report))
}
```
### Diagnostics
An error that carries its own guidance is read without any configuration. `fix` becomes the report's `hint`, a `docs` URL becomes `docsUrl`, and `sources` (`file:line:column` strings) become app frames with snippets, or a `Sources` section when they cannot be resolved to a readable file. An error named after its own `code` is treated as self-describing: the heading shows the code once, and presets leave its `docsUrl` alone, including when it has none.
```ts
class Diagnostic extends Error {
name = 'NUXT_E1001'
code = 'NUXT_E1001'
fix = 'Pass a non-empty `name` prop to ``.'
docs = 'https://nuxt.com/docs/4.x/errors/e1001'
sources = ['pages/index.vue:2:16']
}
```
`environment` labels where the error happened (`'Server'`, `'Client'`, or whatever your integration calls it) next to the error name.
### Overlay
Inject the error UI into an existing page (for example a framework's rendered error page). It renders in a shadow root, so host styles cannot leak in, and can be minimised to a picture-in-picture thumbnail so the user can see the page behind it.
```ts
import { injectOverlay } from 'my-bad'
html = injectOverlay(html, report, { channel: '/__my-bad', startMinimized: status < 500 })
```
`startMinimized: true` always mounts minimised, even if the user last expanded an overlay in this origin. Errors arriving over the channel leave the current minimised state alone.
Pass `requestId` alongside the same id given to `channel.setError` so the channel only sends this page the errors its own request produced. Without it the page's path is matched against the request instead. Errors published with no request (compile errors, client runtime errors) reach every page.
Use `injectOverlay` rather than `html.replace('