Why email reports?
After a CI run finishes you usually have two options: check GitHub Actions manually or wait for a Slack ping with a link. Neither is great when you want the whole team — including non-engineers — to see what broke.
@playwright-labs/reporter-email solves this by hooking into Playwright's reporter API and sending an HTML email as soon as the run completes. No extra server, no external service — just SMTP.
New in this release: shadcn/ui templates
The package now ships 4 new templates built on top of email-safe shadcn/ui components.
"Email-safe" means no Radix UI (requires browser APIs), no CSS variables (not supported in most clients), and
<a>tags instead of<button>for interactive elements.
Template overview
| Template | Import | Key feature |
|---|---|---|
| Base | …/shadcn |
Badge + Card layout |
| Chart | …/shadcn/base-chart |
Pass-rate stacked bar |
| Button | …/shadcn/base-button |
CTA links to report / failures |
| Themes | …/shadcn/base-themes |
6 color themes |
| Select | …/shadcn/base-select |
Status filter |
Subpath exports
Each template ships as its own bundle so you only import what you use:
import { PlaywrightReportShadcnChartEmail }
from "@playwright-labs/reporter-email/templates/shadcn/base-chart";
TypeScript resolves types correctly with "moduleResolution": "bundler" in tsconfig.json.
Quick setup
Install the package:
npm install @playwright-labs/reporter-email
Add to playwright.config.ts:
import type { PlaywrightTestConfig } from "@playwright/test";
const config: PlaywrightTestConfig = {
reporter: [
["@playwright-labs/reporter-email", {
host: "smtp.example.com",
port: 587,
from: "ci@example.com",
to: ["team@example.com"],
template: "shadcn", // or "shadcn-chart", "shadcn-themes", …
statusFilter: ["failed"], // send only failures
}],
],
};
export default config;
The templates
The most useful pattern for busy pipelines: send an email only when something breaks, and show only the failed tests.
import PlaywrightReportShadcnEmail from
"@playwright-labs/reporter-email/templates/shadcn/base-button";
<PlaywrightReportShadcnEmail
result={result}
testCases={testCases}
/>
The email renders a shadcn-like button trigger showing.
Previewing templates locally
The examples/ directory has a dev server powered by react-email:
cd packages/reporter-email/examples
pnpm email:preview
# → http://localhost:3000
In your project:
# install @react-email/components & @react-email/render
pnpm i @react-email/components @react-email/render -D
Create template & Start dev server
mkdir -p ./emails
pnpm exec email dev --dir emails
Create your template:
import React from "react";
import type { FullResult, TestCase, TestResult } from "@playwright/test/reporter";
import PlaywrightReportEmail from "@playwright-labs/reporter-email/templates/base";
// mock data
const result = {
status: "failed",
duration: 14_320,
startTime: new Date(),
} satisfies FullResult;
function tc(title: string, suite: string): TestCase {
return { title, parent: { title: suite } } as unknown as TestCase;
}
function tr(status: TestResult["status"], duration: number): TestResult {
return { status, duration } as TestResult;
}
const testCases: [TestCase, TestResult][] = [
[tc("Login with valid credentials", "Auth"), tr("passed", 342)],
[tc("Login with invalid credentials", "Auth"), tr("passed", 289)],
[tc("Logout", "Auth"), tr("passed", 190)],
[tc("Add item to cart", "Shopping Cart"), tr("failed", 5102)],
[tc("Remove item from cart", "Shopping Cart"), tr("failed", 4320)],
[tc("View product details", "Product"), tr("passed", 421)],
[tc("Filter by category", "Product"), tr("passed", 374)],
[tc("Search by keyword", "Search"), tr("skipped", 0)],
];
// your template!
/** Default export is required by the React Email dev server. */
export default function PlaywrightReportPreview() {
return (
<>
<span> This is an example preview</span>
<PlaywrightReportEmail result={result} testCases={testCases} />
</>
);
}
Check out the repo for all preview files and the full source.

Top comments (0)