status-page-ui
Status Page UI
Build a status page UI using OpenStatus — from a quick React widget to a fully custom page with composable components. Choose the approach that fits your needs.
When to Use
- "build a status page with OpenStatus"
- "add a status page to my app"
- "embed a status widget"
- "integrate OpenStatus UI into my router"
- "show monitor status in my dashboard"
- "fetch status page data with the SDK"
- User wants to render monitors, uptime bars, incident timelines, or maintenance feeds
Choose Your Approach
| Approach | Best For | Effort |
|---|---|---|
| React Widget | Quick embed in any React app | 5 min |
| Component Registry | Custom status page with full control | 30 min |
| Node.js SDK | Programmatic data fetching & management | 20 min |
Approach 1: React Widget (Quickest)
For a simple status indicator, use the @openstatus/react package:
npm install @openstatus/react
React Server Component:
import { StatusWidget } from "@openstatus/react";
export function Page() {
return <StatusWidget slug="your-slug" />;
}
With custom link: <StatusWidget slug="your-slug" href="https://status.yourdomain.com" />
StatusWidgetis an async RSC — it only works with React Server Components.
Custom widget with getStatus:
import { getStatus } from "@openstatus/react";
async function CustomStatus() {
const { status } = await getStatus("your-slug");
// status: "unknown" | "operational" | "degraded_performance"
// | "partial_outage" | "major_outage" | "under_maintenance" | "incident"
return <div>Current status: {status}</div>;
}
Styling:
- With Tailwind: add
"./node_modules/@openstatus/react/**/*.{js,ts,jsx,tsx}"to your content config - Without Tailwind:
import "@openstatus/react/dist/styles.css"
Approach 2: Component Registry (Full Custom UI)
For complete control, install composable components from the OpenStatus shadcn-compatible registry.
Step 1: Install Components
# Everything (recommended for a full status page)
npx shadcn@latest add https://openstatus.dev/r/status-complete.json
# Or only essentials (banner, layout, icons, blank states)
npx shadcn@latest add https://openstatus.dev/r/status-essentials.json
# Or individual components
npx shadcn@latest add https://openstatus.dev/r/status-banner.json
npx shadcn@latest add https://openstatus.dev/r/status-component.json
npx shadcn@latest add https://openstatus.dev/r/status-bar.json
npx shadcn@latest add https://openstatus.dev/r/status-feed.json
Step 2: Add CSS Variables
Add status color variables to your global CSS (required):
@layer base {
:root {
--success: 142.1 76.2% 36.3%;
--success-foreground: 355.7 100% 97.3%;
--warning: 38 92% 50%;
--warning-foreground: 48 96% 89%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--info: 221.2 83.2% 53.3%;
--info-foreground: 210 40% 98%;
}
.dark {
--success: 142.1 70.6% 45.3%;
--success-foreground: 144.9 80.4% 10%;
--warning: 38 92% 50%;
--warning-foreground: 48 96% 89%;
--destructive: 0 72.2% 50.6%;
--destructive-foreground: 0 85.7% 97.3%;
--info: 217.2 91.2% 59.8%;
--info-foreground: 222.2 47.4% 11.2%;
}
}
Extend Tailwind config:
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
success: "hsl(var(--success))",
"success-foreground": "hsl(var(--success-foreground))",
warning: "hsl(var(--warning))",
"warning-foreground": "hsl(var(--warning-foreground))",
info: "hsl(var(--info))",
"info-foreground": "hsl(var(--info-foreground))",
},
},
},
};
Step 3: Fetch Data
Use the OpenStatus SDK (see Approach 3) or your own data source. Components expect these types:
type StatusType = "success" | "degraded" | "error" | "info" | "empty";
interface StatusBarData {
day: string;
bar: { status: StatusType; height: number }[]; // heights must sum to 100%
card: { status: StatusType; value: string }[];
events: {
id: number;
name: string;
type: "incident" | "report" | "maintenance";
from: Date | null;
to: Date | null;
}[];
}
interface StatusReport {
id: number;
title: string;
affected: string[];
updates: {
date: Date;
message: string;
status: "investigating" | "identified" | "monitoring" | "resolved";
}[];
}
interface Maintenance {
id: number;
title: string;
affected: string[];
message: string;
from: Date;
to: Date;
}
Step 4: Compose the Page
import {
Status, StatusHeader, StatusBrand, StatusTitle,
StatusDescription, StatusContent,
} from "@/components/blocks/status-layout";
import { StatusBanner } from "@/components/blocks/status-banner";
import {
StatusComponent, StatusComponentHeader,
StatusComponentHeaderLeft, StatusComponentHeaderRight,
StatusComponentIcon, StatusComponentTitle,
StatusComponentDescription, StatusComponentUptime,
StatusComponentStatus, StatusComponentBody,
StatusComponentFooter,
} from "@/components/blocks/status-component";
import { StatusBar } from "@/components/blocks/status-bar";
import { StatusFeed } from "@/components/blocks/status-feed";
export function MyStatusPage({ monitors, reports, maintenances, overallStatus }) {
return (
<Status variant={overallStatus}>
<StatusHeader>
<div className="flex items-center gap-4">
<StatusBrand src="/logo.png" alt="My Company" />
<div>
<StatusTitle>System Status</StatusTitle>
<StatusDescription>Current health of all services</StatusDescription>
</div>
</div>
</StatusHeader>
<StatusBanner status={overallStatus} />
<StatusContent>
{monitors.map((monitor) => (
<StatusComponent key={monitor.id} variant={monitor.status}>
<StatusComponentHeader>
<StatusComponentHeaderLeft>
<StatusComponentIcon />
<StatusComponentTitle>{monitor.name}</StatusComponentTitle>
<StatusComponentDescription>
{monitor.description}
</StatusComponentDescription>
</StatusComponentHeaderLeft>
<StatusComponentHeaderRight>
<StatusComponentUptime>{monitor.uptime}%</StatusComponentUptime>
<StatusComponentStatus />
</StatusComponentHeaderRight>
</StatusComponentHeader>
<StatusComponentBody>
<StatusBar data={monitor.barData} />
<StatusComponentFooter data={monitor.barData} />
</StatusComponentBody>
</StatusComponent>
))}
</StatusContent>
<StatusFeed statusReports={reports} maintenances={maintenances} />
</Status>
);
}
Step 5: Optional Enhancements
See references/component-api.md for the full component API and references/advanced-patterns.md for collapsible groups, tabbed banners, and custom banner composition.
Approach 3: Node.js SDK
Programmatically manage status pages and fetch data with @openstatus/sdk-node:
npm install @openstatus/sdk-node
Create a Status Page with Components
import {
createOpenStatusClient,
Periodicity,
Region,
} from "@openstatus/sdk-node";
const client = createOpenStatusClient({
apiKey: process.env.OPENSTATUS_API_KEY,
});
// Create a status page
const { statusPage } = await client.statusPage.v1.StatusPageService.createStatusPage({
title: "Acme Status",
slug: "acme-status",
description: "Real-time status of Acme services",
homepageUrl: "https://acme.com",
contactUrl: "mailto:support@acme.com",
});
// Add a monitor component (linked to a live monitor)
await client.statusPage.v1.StatusPageService.addMonitorComponent({
pageId: statusPage!.id,
monitorId: existingMonitorId,
name: "Production API",
description: "Core API endpoint",
order: 1,
});
// Add a static component (manual status only, no linked monitor)
await client.statusPage.v1.StatusPageService.addStaticComponent({
pageId: statusPage!.id,
name: "Third-Party Payment Gateway",
description: "External dependency",
order: 2,
});
// Create a component group
const { group } = await client.statusPage.v1.StatusPageService.createComponentGroup({
pageId: statusPage!.id,
name: "Infrastructure",
});
// Move a component into a group
await client.statusPage.v1.StatusPageService.updateComponent({
id: componentId,
groupId: group!.id,
groupOrder: 1,
});
Fetch Status Page Content
// Get full page content (components, groups, active reports, maintenances)
const content = await client.statusPage.v1.StatusPageService.getStatusPageContent({
identifier: { case: "slug", value: "acme-status" },
});
// Get overall status summary
const { overallStatus, componentStatuses } =
await client.statusPage.v1.StatusPageService.getOverallStatus({
identifier: { case: "slug", value: "acme-status" },
});
Subscriber Management
// Subscribe a user
await client.statusPage.v1.StatusPageService.subscribeToPage({
pageId: statusPage!.id,
email: "user@example.com",
});
// List subscribers
const { subscribers } = await client.statusPage.v1.StatusPageService.listSubscribers({
pageId: statusPage!.id,
limit: 50,
});
// Unsubscribe
await client.statusPage.v1.StatusPageService.unsubscribeFromPage({
pageId: statusPage!.id,
identifier: { case: "email", value: "user@example.com" },
});
Page Components: Monitor vs Static
OpenStatus supports two types of page components:
| Feature | Monitor Component | Static Component |
|---|---|---|
| Linked to a monitor | Yes | No |
| Automatic incidents | Yes | No |
| Manual status reports | Yes | Yes |
| Scheduled maintenance | Yes | Yes |
| Use case | Your own services | Third-party dependencies |
Status hierarchy:
- Monitor: Error > Degraded > Info > Success
- Static: Degraded > Info > Success
Component Groups organize related components into collapsible sections (e.g., "API Services", "Infrastructure", "External Dependencies").
Theming
OpenStatus supports community themes for status pages. Available themes include default, supabase, github-high-contrast, dracula, and more.
- Browse themes at themes.openstatus.dev
- Configure in Dashboard > Status Page settings > Theme Explorer
- Contribute themes by adding CSS variable overrides to the
@openstatus/theme-storepackage
Themes customize: base colors, status colors, brand colors (primary, secondary, muted, accent), and border radius.
Internationalization (i18n)
Status pages support localization (paid plans only):
- Supported locales:
en(English),fr(French),de(German) - Enable: Dashboard > Status Page settings > Locales
- Routing: locale prefix added as-needed (default locale omitted from URL)
- Subdomain:
/status/fr/events - Custom domain:
/fr/events
- Subdomain:
Configuration Options
| Option | Description |
|---|---|
| Slug | URL path (e.g., acme → acme.openstatus.dev) |
| Custom Domain | Your own domain with CNAME to status.openstatus.dev |
| Password Protection | Basic auth; bypass with ?pw=secret |
| Magic Link Auth | Email domain restriction (paid) |
| Favicon | Custom browser tab icon |
| Homepage URL | Where the logo links to |
| Contact URL | "Message" icon link (supports mailto:) |
| White Label | Remove "powered by OpenStatus" footer (paid) |
| JSON Feed | Machine-readable at /feed/json |
| RSS/Atom Feed | Available at /feed/rss and /feed/atom |
| SSH Access | ssh [slug]@ssh.openstatus.dev |
| Tracker Bar Type | absolute or manual |
| Tracker Card Type | duration or requests (when bar is absolute) |
| Show Uptime | Toggle uptime percentage display |
Anti-Patterns
- Don't skip CSS variables when using the component registry. Components use
var(--success),var(--warning), etc. - Don't mix import paths. After registry install, import from
@/components/blocks/..., not@openstatus/ui/.... - Don't hardcode status labels. Use
StatusComponentStatusandStatusBannerMessage— they auto-render from the variant prop. - Don't forget
"use client"directives.StatusComponent,StatusBar, andStatusFeeduse client hooks.
Guidelines
- Start with the React Widget if you just need a quick status indicator.
- Use the Component Registry for a fully branded, custom status page in your own router.
- Use the SDK to programmatically create pages, manage components, and fetch data.
- The
variantprop drives everything. Pass"success","degraded","error", or"info"— child components auto-style via CSS data attributes. - StatusBar is interactive. Supports hover, keyboard navigation, and touch with no extra wiring.
- StatusFeed merges reports and maintenance. Pass both arrays and it handles chronological sorting.
- All components are composable. Every component accepts
classNameand standard div props.
Related Skills
status-page-context— Set up the foundational context document (components, SLAs, tone)incident-communication— Write status updates during incidentsmaintenance— Write maintenance announcementsstatus-report— Write periodic health reportsglobal-speed-checker— Run global HTTP performance checks from 28 regions
More from openstatushq/skills
global-speed-checker
Run global performance checks on HTTP endpoints from multiple regions worldwide. Use when users want to check speed, latency, performance, or test endpoints globally.
18status-report
Write periodic status reports summarizing overall system health, uptime, incidents, and maintenance. Use when the user mentions "status report," "health report," "uptime report," "weekly status," "monthly report," "system health summary," "reliability report," or wants to publish a regular update on how their services are performing.
6maintenance
Write planned maintenance announcements for each phase (scheduled, in-progress, completed). Use when the user mentions "maintenance announcement," "scheduled maintenance," "maintenance window," "planned downtime," "maintenance notification," or needs to communicate upcoming planned work to users.
6postmortem
Write blameless postmortems after incidents with timeline, root cause analysis, impact assessment, and action items. Use when the user mentions "postmortem," "post-mortem," "incident review," "root cause analysis," "RCA," "incident retrospective," "what went wrong," or wants to document lessons from a resolved incident.
6status-page-context
Create or update the status page context document that all other status page skills reference. Use when setting up status page skills for the first time, or when the user mentions "status page context," "configure status page," "set up incident tone," or wants to define their service components, SLAs, or communication style.
6incident-communication
Write clear, empathetic incident status updates for any phase of an incident (investigating, identified, monitoring, resolved). Use when the user mentions "incident update," "status update," "outage communication," "write an incident," "investigating update," "post-incident update," or needs to communicate a service disruption to users.
6