# Share a Jest or Vitest HTML Report — Stable Link, Comments on Failures

Canonical: https://commareports.com/share-jest-report
Published: 2026-08-22

> Jest and Vitest HTML reports end up as CI artifact zips nobody opens. Publish one to Comma with a single curl: a stable URL per suite, threads anchored to failing tests, and a revision for every run.

# Share a Jest or Vitest HTML report

The default JS test report is a terminal scrollback, which is fine until
someone who wasn't watching the terminal needs to see it. Then comes the
HTML reporter, then the artifact upload, then the zip, then — reliably —
a screenshot of the failure pasted into Slack.

The report is fine. It has no address.

## One curl

```bash
npx jest --reporters=default --reporters=jest-html-reporter || true

curl -fsS -X PATCH \
  "https://commareports.com/api/v1/reports/$COMMA_REPORT_ID" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html test-report.html \
        --arg title "Unit tests — $(git rev-parse --short HEAD)" \
        '{title: $title, html: $html}')"
```

`jest-html-reporter` writes one self-contained file, which is the shape
that publishes in a single call. Use a [scoped token](/docs/api-tokens)
(`reports:write`) from CI secrets, and `PATCH` a saved report id rather
than `POST`ing — that keeps one URL with a revision per run instead of a
new orphan link every build.

### Vitest's HTML reporter has a directory

`vitest --reporter=html` writes `html/index.html` plus the result data
beside it, and the page **fetches that data at view time**. Publish the
index as the body and upload the siblings as [assets](/docs/api):

```bash
for f in html/assets/* html/*.json; do
  curl -fsS -X POST \
    "https://commareports.com/api/v1/reports/$COMMA_REPORT_ID/assets" \
    -H "Authorization: Bearer $COMMA_API_TOKEN" -F "file=@$f"
done
```

Relative references are rewritten to the uploaded assets, so the page
finds its data. This is the same pattern that
[Allure](/share-allure-report) and [Gatling](/share-gatling-report) need,
and it's worth knowing once.

## What the URL is for

- **Threads on failing tests.** Highlight the assertion, pin a comment,
  and it survives the next dozen runs — see
  [commenting on HTML](/comment-on-html).
- **Readers outside the repo.** A designer checking a snapshot diff, a PM
  checking an acceptance suite. [Sharing](/docs/sharing) is per report.
- **Diffs between runs.** Which tests flipped since the last green build,
  answered by a revision diff.
- **Slack on every push.** A [webhook](/docs/api) on `revision.created`.

## Limits

- **HTML body: 5 MB.** Inline snapshots and base64 screenshots get there
  faster than you'd expect; move them to assets (25 MB/file, 250 MB/report).
- **Scripts run, sandboxed** — `allow-scripts`, no `allow-same-origin`.
- **60 requests/minute per token.**

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited
revision history.

**[Create your first report →](https://commareports.com/)**

### Related

- [Coverage reports](/share-coverage-report) · [Playwright](/share-playwright-report) · [Cypress](/share-cypress-report)
- [JUnit XML → HTML](/share-junit-report) — the language-agnostic route
- [GitHub Actions](/ci/github-actions-html-report) — `if: always()`, in context
