1. Documentation
  2. Frameworks
  3. Next.js
ReadmenpmGitHub
  • Introduction
  • Quickstart
  • Configuration file
  • Monitors
  • Checks and scheduling
  • Notifications
  • Incidents and maintenance
  • Subscribers
  • Metrics and badges
  • Identity and appearance
  • Telemetry
  • The check run
  • Uptime and history
  • Automatic incidents
  • Notification delivery
  • Public subscriptions
  • StatusPage component
  • Themes
  • UI primitives
  • Colour mode
  • React Router
  • Next.js
  • Astro
  • SQLite
  • Cloudflare D1
  • PostgreSQL
  • Schema and migrations
  • Cloudflare Workers
  • Vercel
  • Self-hosting
  • API overview
  • Status endpoints
  • Incident and maintenance endpoints
  • Subscriber endpoints
  • Badges, feeds and metrics
  • Admin endpoints
  • CLI commands
  • Package exports
  • Security
  • Troubleshooting

Next.js

Mount the App Router route, render the page and schedule checks.

The adapter returns the five method exports an App Router route handler needs. No Next package is imported by Heyo Status, so the module stays safe in every runtime.

bash
bun create @heyo-sh/heyo-status my-status-page --template next

Project shape

  • my-status-page
    • heyo-status.config.ts
    • heyo-status.database.ts
    • app
      • layout.tsx
      • page.tsx
      • api/heyo-status/[...path]
        • route.ts
    • styles
      • status.css
    • next.config.mjs

The API route

app/api/heyo-status/[...path]/route.ts
import { statusHandler } from "@heyo-sh/heyo-status/next";async function handle(  method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE",  request: Request,) {  const { statusPage } = await import("../../../../heyo-status.config");  return statusHandler(statusPage)[method](request);}export const GET = (request: Request) => handle("GET", request);export const POST = (request: Request) => handle("POST", request);export const PATCH = (request: Request) => handle("PATCH", request);export const PUT = (request: Request) => handle("PUT", request);export const DELETE = (request: Request) => handle("DELETE", request);

The simpler form works when the configuration can be imported statically:

ts
import { statusHandler } from "@heyo-sh/heyo-status/next";import { statusPage } from "../../../../heyo-status.config";export const { GET, POST, PATCH, PUT, DELETE } = statusHandler(statusPage);

The generated template uses the dynamic form so an embedded SQLite adapter is never constructed during Next's static analysis.

The page

app/page.tsx
import { StatusPage } from "@heyo-sh/heyo-status/ui";export const dynamic = "force-dynamic";export default async function Page() {  const { statusPage } = await import("../heyo-status.config");  const snapshot = await statusPage.api.getSnapshot();  return (    <StatusPage      basePath={statusPage.basePath}      branding={statusPage.branding}      colors={statusPage.colors}      metrics={statusPage.metrics}      mode={statusPage.mode}      snapshot={snapshot}      subscribersEnabled={statusPage.subscribers.enabled}      subscriberMethods={statusPage.subscribers.methods}      theme={statusPage.theme}      title={statusPage.title}    />  );}
force-dynamic is not optional

A status page must reflect the last check, not the last build. Without export const dynamic = "force-dynamic" Next will happily serve a statically rendered snapshot from deploy time.

Root layout

app/layout.tsx
import { statusThemeBootstrapScript } from "@heyo-sh/heyo-status/theme/script";import type { Metadata } from "next";import "@heyo-sh/heyo-status/theme/status.css";import "../styles/status.css";export async function generateMetadata(): Promise<Metadata> {  const { statusPage } = await import("../heyo-status.config");  return { title: statusPage.title, description: statusPage.description };}export default async function RootLayout({ children }: { children: React.ReactNode }) {  const { statusPage } = await import("../heyo-status.config");  return (    <html lang="en" suppressHydrationWarning>      <head>        <script          dangerouslySetInnerHTML={{            __html: statusThemeBootstrapScript(undefined, statusPage.mode),          }}        />      </head>      <body>{children}</body>    </html>  );}

Scripts

package.json
{  "scripts": {    "dev": "heyo-status dev -- bunx --bun next dev",    "build": "next build",    "start": "next start",    "check": "heyo-status check run",    "db:migrate": "heyo-status db migrate",    "db:seed": "heyo-status db seed --uptime 99.95 --days 90"  }}

Deployment notes

TargetWhat the overlay adds
Cloudflare WorkersOpenNext config, worker/status-worker.ts binding D1 and forwarding scheduled, wrangler.jsonc with three cron triggers.
Vercelapp/api/cron/[task]/route.ts guarded by CRON_SECRET, plus vercel.json.
Self-hostednext start on Node.js 20.19+, with node-cron or an external scheduler.

On Workers, node:net and node:tls are unavailable, so tcp and ssl monitors report as unsupported. On Node, keep the runtime at 20.19 or newer so process.getBuiltinModule can resolve them.

Related

  • Deploying to Vercel
  • Database adapters
  • StatusPage props
React Router< PreviousAstroNext >

Powered by heyo

On this page

Project shapeThe API routeThe pageRoot layoutScriptsDeployment notes