Docs
Status Page

Status Page

A modern system status page for SaaS products. Features real-time service monitoring, incident tracking, and uptime statistics.

System Status

Current status of all our services

Degraded Performance

All systems are running smoothly

99.89%
Uptime (30 days)

Services

REST API

Core REST API endpoints and GraphQL services

Operational

Web Application

Main dashboard and user interface

Degraded Performance

Database Cluster

Primary PostgreSQL database with read replicas

Operational

CDN & Static Assets

Global content delivery network

Maintenance

Authentication Service

OAuth, SSO, and session management

Operational

Email Delivery

Transactional and notification emails

Major Outage

Webhook Service

Event notifications and integrations

Operational

Search Engine

Full-text search and indexing

Operational

File Storage

Object storage and file uploads

Operational

Analytics Pipeline

Data processing and reporting

Operational

Recent Incidents

Email Service Outage

major

November 16, 2024 at 10:15 AM PST

Email delivery has been restored. We're monitoring the service closely to ensure stability.

11:30 AM PST

We've identified a configuration issue with our email provider and are implementing a fix.

10:45 AM PST

We're investigating reports of email delivery failures. Transactional emails are currently not being sent.

10:15 AM PST

CDN Scheduled Maintenance

minor

November 16, 2024 at 9:00 AM PST

Maintenance is progressing as planned. We expect to complete by 12:00 PM PST.

10:00 AM PST

We're performing scheduled maintenance on our CDN infrastructure. Some static assets may load slower than usual.

9:00 AM PST

Web Application Performance Degradation

minor

November 16, 2024 at 8:30 AM PST

We're investigating reports of slower page load times in the dashboard. API services are not affected.

8:30 AM PST

Last updated: 11/17/2025, 4:45:23 PM

Subscribe to updates

Installation

Install dependencies

npm install framer-motion lucide-react

Copy and paste the following code into your project.

"use client";
 
import React from "react";
import { motion } from "framer-motion";
import { CheckCircle2, AlertCircle, XCircle, Clock } from "lucide-react";
 
interface ServiceStatus {
  id: string;
  name: string;
  description?: string;
  status: "operational" | "degraded" | "outage" | "maintenance";
  uptime?: string;
}
 
interface Incident {
  id: string;
  title: string;
  status: "investigating" | "identified" | "monitoring" | "resolved";
  severity: "minor" | "major" | "critical";
  timestamp: string;
  updates: {
    message: string;
    timestamp: string;
  }[];
}
 
interface StatusPageProps {
  title?: string;
  description?: string;
  overallStatus?: "operational" | "degraded" | "outage";
  services: ServiceStatus[];
  incidents?: Incident[];
  uptimePercentage?: string;
  className?: string;
}
 
export function StatusPage({
  title = "System Status",
  description = "Current status of all our services",
  overallStatus = "operational",
  services,
  incidents = [],
  uptimePercentage = "99.99",
  className = "",
}: StatusPageProps) {
  const getStatusConfig = (status: ServiceStatus["status"]) => {
    switch (status) {
      case "operational":
        return {
          label: "Operational",
          color: "text-emerald-600 dark:text-emerald-400",
          bg: "bg-emerald-50 dark:bg-emerald-950/30",
          dot: "bg-emerald-500",
          icon: CheckCircle2,
        };
      case "degraded":
        return {
          label: "Degraded Performance",
          color: "text-amber-600 dark:text-amber-400",
          bg: "bg-amber-50 dark:bg-amber-950/30",
          dot: "bg-amber-500",
          icon: AlertCircle,
        };
      case "outage":
        return {
          label: "Major Outage",
          color: "text-red-600 dark:text-red-400",
          bg: "bg-red-50 dark:bg-red-950/30",
          dot: "bg-red-500",
          icon: XCircle,
        };
      case "maintenance":
        return {
          label: "Maintenance",
          color: "text-blue-600 dark:text-blue-400",
          bg: "bg-blue-50 dark:bg-blue-950/30",
          dot: "bg-blue-500",
          icon: Clock,
        };
    }
  };
 
  const overallConfig = getStatusConfig(
    overallStatus === "operational"
      ? "operational"
      : overallStatus === "degraded"
        ? "degraded"
        : "outage",
  );
  const OverallIcon = overallConfig.icon;
 
  return (
    <section className={`w-full py-24 ${className}`}>
      <div className="mx-auto max-w-5xl px-4 sm:px-6 lg:px-8">
        {/* Header */}
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.5, ease: "easeOut" }}
          className="mb-12 text-center"
        >
          <h2 className="mb-4 text-4xl font-bold tracking-tight text-zinc-900 dark:text-white sm:text-5xl">
            {title}
          </h2>
          <p className="mx-auto max-w-2xl text-lg text-zinc-600 dark:text-zinc-400">
            {description}
          </p>
        </motion.div>
 
        {/* Overall Status Banner */}
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.5, delay: 0.1, ease: "easeOut" }}
          className={`mb-12 overflow-hidden rounded-2xl border p-8 ${overallConfig.bg} ${
            overallStatus === "operational"
              ? "border-emerald-200 dark:border-emerald-800"
              : overallStatus === "degraded"
                ? "border-amber-200 dark:border-amber-800"
                : "border-red-200 dark:border-red-800"
          }`}
        >
          <div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-between">
            <div className="flex items-center gap-4">
              <div
                className={`flex h-12 w-12 items-center justify-center rounded-full ${overallConfig.bg}`}
              >
                <OverallIcon className={`h-6 w-6 ${overallConfig.color}`} />
              </div>
              <div>
                <h3 className={`text-2xl font-bold ${overallConfig.color}`}>
                  {overallConfig.label}
                </h3>
                <p className="text-sm text-zinc-600 dark:text-zinc-400">
                  All systems are running smoothly
                </p>
              </div>
            </div>
            <div className="text-center sm:text-right">
              <div className="text-3xl font-bold text-zinc-900 dark:text-white">
                {uptimePercentage}%
              </div>
              <div className="text-sm text-zinc-600 dark:text-zinc-400">
                Uptime (30 days)
              </div>
            </div>
          </div>
        </motion.div>
 
        {/* Services Status */}
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.5, delay: 0.2, ease: "easeOut" }}
          className="mb-12"
        >
          <h3 className="mb-6 text-xl font-bold text-zinc-900 dark:text-white">
            Services
          </h3>
          <div className="space-y-3">
            {services.map((service, index) => {
              const config = getStatusConfig(service.status);
              const Icon = config.icon;
 
              return (
                <motion.div
                  key={service.id}
                  initial={{ opacity: 0, x: -20 }}
                  animate={{ opacity: 1, x: 0 }}
                  transition={{
                    duration: 0.4,
                    delay: 0.3 + index * 0.05,
                    ease: "easeOut",
                  }}
                  className="flex items-center justify-between rounded-xl border border-zinc-200 bg-white p-4 transition-all hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900"
                >
                  <div className="flex items-center gap-4">
                    <div
                      className={`flex h-10 w-10 items-center justify-center rounded-lg ${config.bg}`}
                    >
                      <Icon className={`h-5 w-5 ${config.color}`} />
                    </div>
                    <div>
                      <h4 className="font-semibold text-zinc-900 dark:text-white">
                        {service.name}
                      </h4>
                      {service.description && (
                        <p className="text-sm text-zinc-600 dark:text-zinc-400">
                          {service.description}
                        </p>
                      )}
                    </div>
                  </div>
                  <div className="flex items-center gap-3">
                    {service.uptime && (
                      <span className="hidden text-sm font-medium text-zinc-600 dark:text-zinc-400 sm:block">
                        {service.uptime}
                      </span>
                    )}
                    <span
                      className={`inline-flex items-center gap-2 rounded-full px-3 py-1 text-sm font-semibold ${config.bg} ${config.color}`}
                    >
                      <span className={`h-2 w-2 rounded-full ${config.dot}`} />
                      {config.label}
                    </span>
                  </div>
                </motion.div>
              );
            })}
          </div>
        </motion.div>
 
        {/* Incidents */}
        {incidents.length > 0 && (
          <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.5, delay: 0.4, ease: "easeOut" }}
          >
            <h3 className="mb-6 text-xl font-bold text-zinc-900 dark:text-white">
              Recent Incidents
            </h3>
            <div className="space-y-4">
              {incidents.map((incident, index) => (
                <motion.div
                  key={incident.id}
                  initial={{ opacity: 0, x: -20 }}
                  animate={{ opacity: 1, x: 0 }}
                  transition={{
                    duration: 0.4,
                    delay: 0.5 + index * 0.05,
                    ease: "easeOut",
                  }}
                  className="overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-900"
                >
                  <div className="border-b border-zinc-100 p-6 dark:border-zinc-800">
                    <div className="mb-2 flex items-start justify-between gap-4">
                      <h4 className="text-lg font-bold text-zinc-900 dark:text-white">
                        {incident.title}
                      </h4>
                      <span
                        className={`inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold ${
                          incident.severity === "critical"
                            ? "bg-red-50 text-red-600 dark:bg-red-950/30 dark:text-red-400"
                            : incident.severity === "major"
                              ? "bg-amber-50 text-amber-600 dark:bg-amber-950/30 dark:text-amber-400"
                              : "bg-blue-50 text-blue-600 dark:bg-blue-950/30 dark:text-blue-400"
                        }`}
                      >
                        {incident.severity}
                      </span>
                    </div>
                    <p className="text-sm text-zinc-600 dark:text-zinc-400">
                      {incident.timestamp}
                    </p>
                  </div>
                  <div className="space-y-4 p-6">
                    {incident.updates.map((update, idx) => (
                      <div key={idx} className="flex gap-3 items-start">
                        <div className="">
                          <div className="h-2 w-2 rounded-full bg-zinc-400 dark:bg-zinc-600" />
                        </div>
                        <div className="flex-1 -mt-1.5">
                          <p className=" text-sm text-zinc-900 dark:text-white">
                            {update.message}
                          </p>
                          <p className="text-xs text-zinc-500 dark:text-zinc-500">
                            {update.timestamp}
                          </p>
                        </div>
                      </div>
                    ))}
                  </div>
                </motion.div>
              ))}
            </div>
          </motion.div>
        )}
 
        {/* Footer */}
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          transition={{ duration: 0.5, delay: 0.6, ease: "easeOut" }}
          className="mt-12 text-center"
        >
          <p className="text-sm text-zinc-600 dark:text-zinc-400">
            Last updated: {new Date().toLocaleString()}
          </p>
          <a
            href="#"
            className="mt-2 inline-flex items-center gap-2 text-sm font-semibold text-blue-600 transition-colors hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
          >
            Subscribe to updates
          </a>
        </motion.div>
      </div>
    </section>
  );
}

Features

  • Overall status banner - Shows system-wide health at a glance
  • Service monitoring - Individual status for each service
  • Color-coded status - Operational, Degraded, Outage, Maintenance
  • Uptime percentage - Shows 30-day uptime stats
  • Incident tracking - Display recent incidents with updates
  • Severity levels - Minor, Major, Critical incidents
  • Clean design - Modern, professional appearance
  • Responsive - Works on all screen sizes
  • Dark mode ready - Beautiful in both themes
  • TypeScript support - Full type safety

Usage

Basic Usage

import StatusPage from "@/components/ui/status-page";
 
const services = [
  {
    id: "api",
    name: "REST API",
    description: "Core REST API endpoints and GraphQL services",
    status: "operational",
    uptime: "100%",
  },
  {
    id: "web-app",
    name: "Web Application",
    description: "Main dashboard and user interface",
    status: "operational",
    uptime: "99.99%",
  },
  {
    id: "database",
    name: "Database Cluster",
    description: "Primary PostgreSQL database with read replicas",
    status: "operational",
    uptime: "99.99%",
  },
];
 
export default function Page() {
  return (
    <StatusPage
      overallStatus="operational"
      services={services}
      uptimePercentage="99.99"
    />
  );
}

With Incidents

const incidents = [
  {
    id: "inc-003",
    title: "Email Service Outage",
    status: "monitoring",
    severity: "major",
    timestamp: "November 16, 2024 at 10:15 AM PST",
    updates: [
      {
        message:
          "Email delivery has been restored. We're monitoring the service closely.",
        timestamp: "11:30 AM PST",
      },
      {
        message:
          "We've identified a configuration issue and are implementing a fix.",
        timestamp: "10:45 AM PST",
      },
      {
        message: "We're investigating reports of email delivery failures.",
        timestamp: "10:15 AM PST",
      },
    ],
  },
];
 
<StatusPage
  services={services}
  incidents={incidents}
  overallStatus="degraded"
/>;

Props

StatusPageProps

PropTypeDefaultDescription
servicesServiceStatus[]RequiredArray of services
overallStatus"operational" | "degraded" | "outage""operational"Overall system status
titlestring"System Status"Section title
descriptionstring"Current status of..."Section description
incidentsIncident[][]Array of incidents
uptimePercentagestring"99.99"30-day uptime percentage
classNamestring""Additional CSS classes

ServiceStatus

PropTypeRequiredDescription
idstringYesUnique identifier
namestringYesService name
descriptionstringNoService description
status"operational" | "degraded" | "outage" | "maintenance"YesCurrent status
uptimestringNoUptime percentage

Incident

PropTypeRequiredDescription
idstringYesUnique identifier
titlestringYesIncident title
status"investigating" | "identified" | "monitoring" | "resolved"YesIncident status
severity"minor" | "major" | "critical"YesSeverity level
timestampstringYesIncident timestamp
updates{message: string, timestamp: string}[]YesStatus updates

TypeScript Interface

interface ServiceStatus {
  id: string;
  name: string;
  description?: string;
  status: "operational" | "degraded" | "outage" | "maintenance";
  uptime?: string;
}
 
interface Incident {
  id: string;
  title: string;
  status: "investigating" | "identified" | "monitoring" | "resolved";
  severity: "minor" | "major" | "critical";
  timestamp: string;
  updates: {
    message: string;
    timestamp: string;
  }[];
}
 
interface StatusPageProps {
  title?: string;
  description?: string;
  overallStatus?: "operational" | "degraded" | "outage";
  services: ServiceStatus[];
  incidents?: Incident[];
  uptimePercentage?: string;
  className?: string;
}

Use Cases

Perfect for:

  • Public status pages - Build trust with transparent service status
  • Internal system monitoring - Track infrastructure health
  • Customer transparency - Show real-time service availability
  • SLA reporting - Display uptime metrics and commitments
  • Incident communication - Keep users informed during outages
  • Service health dashboards - Monitor multiple services at once
  • Trust building - Demonstrate reliability and transparency
  • Support pages - Reduce support tickets with proactive updates

Status Types

Operational

  • Color: Green (Emerald)
  • Icon: CheckCircle
  • Use: Service is working normally

Degraded Performance

  • Color: Amber
  • Icon: AlertCircle
  • Use: Service is slow but functional

Major Outage

  • Color: Red
  • Icon: XCircle
  • Use: Service is down

Maintenance

  • Color: Blue
  • Icon: Clock
  • Use: Scheduled maintenance

Customization

Change Status Colors

case "operational":
  return {
    label: "Operational",
    color: "text-green-600 dark:text-green-400", // Your brand color
    bg: "bg-green-50 dark:bg-green-950/30",
    dot: "bg-green-500",
    icon: CheckCircle2,
  };

Add Historical Uptime

// Add uptime history visualization
<div className="mt-8">
  <h4 className="mb-4 font-semibold">90-Day Uptime History</h4>
  <div className="flex gap-1">
    {Array.from({length: 90}).map((_, i) => (
      <div
        key={i}
        className="h-8 w-1 rounded-sm bg-emerald-500"
        title="100% uptime"
      />
    ))}
  </div>
</div>

Add Subscribe Button

<button className="mt-4 rounded-lg bg-blue-600 px-6 py-2 font-semibold text-white hover:bg-blue-700">
  Subscribe to Updates
</button>

Real-Time Updates

"use client";
 
import {useEffect, useState} from "react";
 
export default function LiveStatusPage() {
  const [services, setServices] = useState(initialServices);
 
  useEffect(() => {
    // Poll for updates every 30 seconds
    const interval = setInterval(async () => {
      const response = await fetch("/api/status");
      const data = await response.json();
      setServices(data.services);
    }, 30000);
 
    return () => clearInterval(interval);
  }, []);
 
  return <StatusPage services={services} />;
}

Common Patterns

Complete Status Page

const statusData = {
  overallStatus: "operational",
  services: [
    {
      id: "api",
      name: "REST API",
      description: "Core REST API endpoints and GraphQL services",
      status: "operational",
      uptime: "100%",
    },
    {
      id: "web",
      name: "Web Application",
      description: "Main dashboard and user interface",
      status: "operational",
      uptime: "99.99%",
    },
    {
      id: "db",
      name: "Database Cluster",
      description: "Primary PostgreSQL database with read replicas",
      status: "operational",
      uptime: "100%",
    },
    {
      id: "cdn",
      name: "CDN & Static Assets",
      description: "Global content delivery network",
      status: "operational",
      uptime: "99.95%",
    },
  ],
  incidents: [],
  uptimePercentage: "99.99",
};
 
<StatusPage {...statusData} />;

During an Outage

const outageData = {
  overallStatus: "outage",
  services: [
    {
      id: "api",
      name: "REST API",
      description: "Core REST API endpoints and GraphQL services",
      status: "outage",
      uptime: "98.50%",
    },
    {
      id: "web",
      name: "Web Application",
      description: "Main dashboard and user interface",
      status: "degraded",
      uptime: "99.20%",
    },
    {
      id: "database",
      name: "Database Cluster",
      description: "Primary PostgreSQL database with read replicas",
      status: "operational",
      uptime: "99.99%",
    },
  ],
  incidents: [
    {
      id: "inc-123",
      title: "API Service Disruption",
      status: "investigating",
      severity: "critical",
      timestamp: "November 16, 2024 at 10:00 AM PST",
      updates: [
        {
          message:
            "We're investigating reports of API errors affecting all endpoints.",
          timestamp: "10:00 AM PST",
        },
      ],
    },
  ],
  uptimePercentage: "98.50",
};

Maintenance Mode

const maintenanceData = {
  overallStatus: "degraded",
  services: [
    {
      id: "cdn",
      name: "CDN & Static Assets",
      description: "Global content delivery network",
      status: "maintenance",
      uptime: "99.95%",
    },
    {
      id: "api",
      name: "REST API",
      description: "Core REST API endpoints and GraphQL services",
      status: "operational",
      uptime: "100%",
    },
    {
      id: "web",
      name: "Web Application",
      description: "Main dashboard and user interface",
      status: "operational",
      uptime: "99.99%",
    },
  ],
  incidents: [
    {
      id: "maint-001",
      title: "CDN Scheduled Maintenance",
      status: "identified",
      severity: "minor",
      timestamp: "November 16, 2024 at 2:00 AM PST",
      updates: [
        {
          message:
            "Maintenance is progressing as planned. Expected completion: 4:00 AM PST.",
          timestamp: "3:00 AM PST",
        },
        {
          message:
            "We're performing scheduled maintenance on our CDN infrastructure. Some static assets may load slower than usual.",
          timestamp: "2:00 AM PST",
        },
      ],
    },
  ],
  uptimePercentage: "99.89",
};

Integration

With Monitoring Service

// Fetch status from monitoring service
async function getServiceStatus() {
  const response = await fetch("https://api.monitoring.com/status");
  const data = await response.json();
 
  return data.services.map((service) => ({
    id: service.id,
    name: service.name,
    status: service.healthy ? "operational" : "outage",
    uptime: service.uptime,
  }));
}

With Webhook Notifications

// Send webhook when status changes
async function notifyStatusChange(service: ServiceStatus) {
  await fetch("https://hooks.slack.com/services/YOUR/WEBHOOK/URL", {
    method: "POST",
    body: JSON.stringify({
      text: `${service.name} is now ${service.status}`,
    }),
  });
}