express-pretty-errors
v1.0.2
Published
Pretty HTML error pages and helpers for Express (404 + 500), with JSON fallback.
Maintainers
Readme
express-pretty-errors
🧭 Elegant, dark/light-mode–aware error pages for Express — with JSON fallback, theme & logo support.
✨ Features
- 🔥 Beautiful built-in HTML for
404and500+errors - 🌗 Auto / Light / Dark themes (follows system or forced)
- 🎨 Custom colors via CSS variable overrides
- 🪪 Optional brand logo, name, and accent color
- 🖱️ Theme toggle button (saves preference in
localStorage) - 🧰 Helper utilities:
asyncWrap(fn)for async route errorsrenderWithNext(res, view, data, next)for safe template rendering
- 💬 JSON fallback for API clients
- ⚙️ No dependencies beyond Express
🚀 Install
npm install express-pretty-errors
# or
yarn add express-pretty-errorsYour project must already have Express installed (declared as a peer dependency).
🧩 Quickstart
const express = require("express");
const { notFound, errorHandler } = require("express-pretty-errors");
const app = express();
// Example route that throws an error
app.get("/boom", () => { throw new Error("Kaboom!"); });
// Your normal routes go here...
// 404 handler (no route matched)
app.use(notFound());
// 500+ handler (something threw)
app.use(errorHandler({ showStack: "dev" }));
app.listen(3000, () => console.log("→ http://localhost:3000"));Visit:
/boom→ shows a pretty 500 error page/nope→ shows a pretty 404 page
⚙️ API Reference
notFound(options?)
Handles missing routes (404 Not Found).
| Option | Type | Default | Description |
|--------|------|----------|-------------|
| theme | "auto" \| "light" \| "dark" | "auto" | Force a theme or follow OS setting |
| cssVars | object | — | Override CSS variables (see below) |
| showThemeToggle | boolean | false | Adds a light/dark/auto toggle button |
| logoUrl | string | — | URL or path to logo image (SVG/PNG) |
| logoAlt | string | "Logo" | Alt text for the logo |
| logoHref | string | "/" | Click-through URL for the logo |
| logoHeight | number | 28 | Max height in px |
| brandName | string | — | Optional text label next to logo |
| links | [{ href, label }] | — | Helpful quick links section |
| jsonOnly | boolean | false | Always respond with JSON instead of HTML |
errorHandler(options?)
Handles thrown errors or rejected promises (500+).
Same options as notFound, plus:
| Option | Type | Default | Description |
|--------|------|----------|-------------|
| showStack | "always" \| "never" \| "dev" | "dev" | Show stack traces in development only |
asyncWrap(fn)
Wraps async route handlers to automatically catch rejections.
const { asyncWrap } = require("express-pretty-errors");
app.get("/data", asyncWrap(async (req, res) => {
const rows = await db.query("SELECT * FROM items");
res.json(rows);
}));No need for try/catch—errors go straight to errorHandler.
renderWithNext(res, view, data, next)
Wraps res.render safely; forwards template errors to your errorHandler.
const { renderWithNext } = require("express-pretty-errors");
app.get("/about", (req, res, next) => {
renderWithNext(res, "about", { title: "About Us" }, next);
});🎨 Customizing the Look
A) Theme
app.use(errorHandler({ theme: "dark" }));
app.use(notFound({ theme: "light" }));B) Accent Colors or Backgrounds
app.use(errorHandler({
theme: "auto",
cssVars: {
accent: "#10b981", // teal accent
panel: "#111827", // custom dark panel
light_accent: "#059669" // light mode accent
}
}));C) Add Your Logo & Brand
app.use(errorHandler({
theme: "auto",
logoUrl: "/assets/logo.svg",
logoAlt: "Acme Inc.",
logoHref: "/",
brandName: "Acme Inc.",
showThemeToggle: true
}));D) Example Links Section
notFound({
links: [
{ href: "/", label: "Home" },
{ href: "/contact", label: "Contact Support" },
{ href: "/search?q=", label: "Search" }
]
});🌗 How “Auto” Theme Works
- No
data-themeattribute is set on<html>. - The stylesheet uses
@media (prefers-color-scheme: light)to detect system preference. - If the user clicks the toggle, it sets
data-theme="light"or"dark"and stores the preference inlocalStorage. - That attribute overrides the media query the next time the page loads.
📦 Example Project Structure
express-pretty-errors/
├─ package.json
├─ index.js
├─ template.js
├─ README.md
└─ examples/
├─ public/
│ └─ logo.svg
└─ server.jsexamples/server.js
const express = require("express");
const { notFound, errorHandler } = require("..");
const app = express();
app.use(express.static(__dirname + "/public"));
app.get("/", (_req, res) => res.send(`<a href="/boom">500</a> • <a href="/nope">404</a>`));
app.get("/boom", () => { throw new Error("Kaboom!"); });
app.use(notFound({
theme: "auto",
logoUrl: "/logo.svg",
brandName: "Acme",
showThemeToggle: true
}));
app.use(errorHandler({
showStack: "dev",
theme: "auto",
logoUrl: "/logo.svg",
brandName: "Acme",
showThemeToggle: true
}));
app.listen(3000, () => console.log("→ http://localhost:3000"));🧪 Testing
Run the example:
node examples/server.jsThen visit:
- http://localhost:3000/boom → 500 page
- http://localhost:3000/nope → 404 page
Or test JSON responses:
curl -H "Accept: application/json" http://localhost:3000/boom🧠 Understanding 404 vs 500
| Code | Type | Who’s at Fault | Description | |------|------|----------------|--------------| | 404 | Client Error | The client | The route/resource doesn’t exist. | | 500 | Server Error | The server | Something broke while handling a valid request. |
Your notFound() middleware catches 404s.
Your errorHandler() middleware catches 500s and other thrown errors.
🪪 License
MIT © [Your Name]
💡 Credits
Designed and maintained by CloseRange (Michael Hulbert) Inspired by a desire for nicer default Express error pages.
