Status Pages

Embedding Status Pages: Displaying Service Status in Your Application

Learn how to embed status page widgets, use status APIs, and display real-time service status directly in your application, dashboard, or documentation.

AzMonitor TeamSeptember 3, 20257 min read · 1,360 wordsUpdated January 20, 2026
status page embedstatus widgetstatus APIin-app status

Embedding status information directly in your application puts reliability transparency where customers already are — inside your product — rather than requiring them to navigate to a separate status page. When a customer sees an error, an embedded status indicator immediately tells them whether it's a known issue or something specific to their account, dramatically reducing support tickets and improving customer experience.

Why Embed Status Information

Reduces support tickets — The most common support ticket during an outage is "is your service down?" An in-app status indicator answers this instantly.

Increases transparency — Customers feel better informed when status is visible in their workflow, not buried in a separate URL they have to remember.

Builds trust — Proactively showing degraded status (rather than hiding it) signals confidence in your reliability and willingness to be honest.

Better incident UX — Customers can correlate what they're experiencing with your known status without leaving your product.

Embedding Options

1. Status Widget (JavaScript Embed)

The simplest embed — a JavaScript snippet that displays a small status badge:

<!-- Basic status badge embed -->
<!-- Place in your app header or footer -->
<div id="status-badge"></div>

<script>
  // Load the status page widget asynchronously
  (function() {
    var statusScript = document.createElement('script');
    statusScript.src = 'https://status.example.com/embed/v1/badge.js';
    statusScript.async = true;
    statusScript.dataset.statusPageId = 'your-page-id';
    statusScript.dataset.theme = 'light'; // or 'dark'
    statusScript.dataset.position = 'inline'; // or 'fixed-bottom'
    document.head.appendChild(statusScript);
  })();
</script>

The badge automatically shows the current status and links to the full status page:

/* Status badge visual states */
.status-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 500;
  text-decoration: none;
}

.status-badge.operational { 
  background: #f0fdf4; 
  color: #15803d; 
  border: 1px solid #bbf7d0; 
}

.status-badge.degraded { 
  background: #fffbeb; 
  color: #92400e; 
  border: 1px solid #fde68a; 
}

.status-badge.outage { 
  background: #fef2f2; 
  color: #991b1b; 
  border: 1px solid #fecaca; 
}

.status-indicator {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
}

2. Status API Integration

For more control, use the status page API directly:

// status-client.js
class StatusPageClient {
  constructor(apiUrl, pageId) {
    this.apiUrl = apiUrl;
    this.pageId = pageId;
    this.cache = null;
    this.cacheExpiry = 0;
  }
  
  async getCurrentStatus() {
    // Cache for 60 seconds to avoid hammering the API
    if (this.cache && Date.now() < this.cacheExpiry) {
      return this.cache;
    }
    
    try {
      const response = await fetch(
        `${this.apiUrl}/v1/pages/${this.pageId}/status`,
        {
          headers: { 'Accept': 'application/json' }
        }
      );
      
      if (!response.ok) {
        throw new Error(`Status API returned ${response.status}`);
      }
      
      const data = await response.json();
      
      this.cache = data;
      this.cacheExpiry = Date.now() + 60_000; // Cache 60 seconds
      
      return data;
      
    } catch (error) {
      // Fail gracefully — don't let status fetch crash the app
      console.warn('Status page fetch failed:', error.message);
      return null;
    }
  }
  
  async getComponentStatus(componentId) {
    const status = await this.getCurrentStatus();
    if (!status) return null;
    
    return status.components.find(c => c.id === componentId);
  }
  
  async getActiveIncidents() {
    try {
      const response = await fetch(
        `${this.apiUrl}/v1/pages/${this.pageId}/incidents/active`
      );
      return await response.json();
    } catch {
      return null;
    }
  }
}

// Usage
const statusClient = new StatusPageClient(
  'https://status.example.com/api',
  'your-page-id'
);

// Show status in app header
async function updateStatusBadge() {
  const status = await statusClient.getCurrentStatus();
  
  if (!status) return; // Silent fail if API unavailable
  
  const badge = document.getElementById('status-badge');
  const overallStatus = status.overall_status; // "operational", "degraded", "outage"
  
  badge.className = `status-badge ${overallStatus}`;
  badge.href = 'https://status.example.com';
  badge.innerHTML = `
    <span class="status-indicator"></span>
    ${formatStatusText(overallStatus)}
  `;
}

function formatStatusText(status) {
  const labels = {
    'operational': 'All Systems Operational',
    'degraded': 'Partial Degradation',
    'partial_outage': 'Partial Outage',
    'major_outage': 'Service Disruption',
    'maintenance': 'Scheduled Maintenance'
  };
  return labels[status] || 'Status Unknown';
}

// Refresh every 60 seconds
updateStatusBadge();
setInterval(updateStatusBadge, 60_000);

3. In-App Incident Banner

Display active incidents directly in your application UI:

// incident-banner.js
class IncidentBanner {
  constructor(statusClient, containerId) {
    this.statusClient = statusClient;
    this.container = document.getElementById(containerId);
    this.dismissedIncidents = new Set(
      JSON.parse(localStorage.getItem('dismissed-incidents') || '[]')
    );
  }
  
  async render() {
    const incidents = await this.statusClient.getActiveIncidents();
    
    if (!incidents || incidents.length === 0) {
      this.container.style.display = 'none';
      return;
    }
    
    // Show undismissed incidents
    const visibleIncidents = incidents.filter(
      i => !this.dismissedIncidents.has(i.id)
    );
    
    if (visibleIncidents.length === 0) {
      this.container.style.display = 'none';
      return;
    }
    
    const mostSevere = this.getMostSevereIncident(visibleIncidents);
    
    this.container.style.display = 'block';
    this.container.innerHTML = this.buildBannerHTML(mostSevere, visibleIncidents.length);
    
    // Attach dismiss handler
    const dismissBtn = this.container.querySelector('.banner-dismiss');
    if (dismissBtn) {
      dismissBtn.addEventListener('click', () => {
        visibleIncidents.forEach(i => this.dismissedIncidents.add(i.id));
        localStorage.setItem(
          'dismissed-incidents',
          JSON.stringify([...this.dismissedIncidents])
        );
        this.render();
      });
    }
  }
  
  buildBannerHTML(incident, totalCount) {
    const statusColors = {
      'investigating': '#f59e0b',
      'identified': '#f59e0b',
      'monitoring': '#3b82f6',
      'resolved': '#22c55e'
    };
    
    const bgColor = statusColors[incident.status] || '#f59e0b';
    
    return `
      <div class="incident-banner" style="background: ${bgColor}15; border-left: 4px solid ${bgColor}; padding: 12px 16px; display: flex; align-items: center; gap: 12px;">
        <span class="banner-icon">⚠️</span>
        <div class="banner-content" style="flex: 1;">
          <strong>${incident.title}</strong>
          ${totalCount > 1 ? `<span> and ${totalCount - 1} other incident(s)</span>` : ''}
          <span class="banner-status"> — ${incident.status}</span>
        </div>
        <a href="https://status.example.com" target="_blank" class="banner-link" style="font-size: 12px;">
          View Details →
        </a>
        <button class="banner-dismiss" style="background: none; border: none; cursor: pointer; padding: 4px; font-size: 18px;">×</button>
      </div>
    `;
  }
  
  getMostSevereIncident(incidents) {
    const severity = {
      'critical': 4, 'major': 3, 'minor': 2, 'maintenance': 1
    };
    return incidents.sort((a, b) => 
      (severity[b.severity] || 0) - (severity[a.severity] || 0)
    )[0];
  }
}

// Initialize
const statusClient = new StatusPageClient('https://status.example.com/api', 'page-id');
const banner = new IncidentBanner(statusClient, 'incident-banner-container');
banner.render();
setInterval(() => banner.render(), 60_000);

4. React Component

For React applications:

// StatusBadge.jsx
import { useState, useEffect } from 'react';

const STATUS_COLORS = {
  operational: { bg: '#f0fdf4', text: '#15803d', dot: '#22c55e' },
  degraded: { bg: '#fffbeb', text: '#92400e', dot: '#f59e0b' },
  partial_outage: { bg: '#fff7ed', text: '#9a3412', dot: '#f97316' },
  major_outage: { bg: '#fef2f2', text: '#991b1b', dot: '#ef4444' },
  maintenance: { bg: '#f0f9ff', text: '#075985', dot: '#0ea5e9' },
};

const STATUS_LABELS = {
  operational: 'All Systems Operational',
  degraded: 'Partial Degradation',
  partial_outage: 'Partial Outage',
  major_outage: 'Service Disruption',
  maintenance: 'Maintenance',
};

export function StatusBadge({ statusPageUrl, pollingIntervalMs = 60000 }) {
  const [status, setStatus] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    const fetchStatus = async () => {
      try {
        const res = await fetch(`${statusPageUrl}/api/v1/status`);
        if (res.ok) {
          const data = await res.json();
          setStatus(data.overall_status);
        }
      } catch {
        // Fail silently — status badge shouldn't break the app
      } finally {
        setLoading(false);
      }
    };
    
    fetchStatus();
    const interval = setInterval(fetchStatus, pollingIntervalMs);
    return () => clearInterval(interval);
  }, [statusPageUrl, pollingIntervalMs]);
  
  if (loading || !status) return null;
  
  const colors = STATUS_COLORS[status] || STATUS_COLORS.operational;
  const label = STATUS_LABELS[status] || 'Status Unknown';
  
  return (
    <a
      href={statusPageUrl}
      target="_blank"
      rel="noopener noreferrer"
      style={{
        display: 'inline-flex',
        alignItems: 'center',
        gap: '6px',
        padding: '4px 10px',
        borderRadius: '4px',
        background: colors.bg,
        color: colors.text,
        fontSize: '12px',
        fontWeight: 500,
        textDecoration: 'none',
        border: `1px solid ${colors.dot}40`
      }}
    >
      <span style={{
        width: '8px',
        height: '8px',
        borderRadius: '50%',
        backgroundColor: colors.dot,
        display: 'inline-block'
      }} />
      {label}
    </a>
  );
}

// Usage in app:
// <StatusBadge statusPageUrl="https://status.yourcompany.com" />

Where to Place Status Information

| Location | What to Show | When to Show | |---|---|---| | App header | Small status badge | Always | | Login page | Active incident banner | Only during incidents | | Error pages | Relevant component status | Always | | Documentation | API status widget | Always | | Pricing/Features page | Current uptime percentage | Always | | Admin dashboard | Full component status | Always | | Email footers | Link to status page | Always |

Graceful Degradation

Status embeds should never break your application if the status API is unreachable:

// Always wrap status fetches with error handling
async function safeGetStatus() {
  try {
    const response = await fetch('/status-api', { 
      timeout: 3000,  // Short timeout
      signal: AbortSignal.timeout(3000)
    });
    return await response.json();
  } catch {
    return null;  // Silently fail — don't crash the app
  }
}

// Check before rendering
const status = await safeGetStatus();
if (status) {
  renderStatusBadge(status);
}
// If null, badge simply doesn't render — clean graceful degradation

Conclusion

Embedding status information in your application closes the loop between what customers experience and what your status page communicates. The implementation is straightforward — a JavaScript widget, an API integration, or a React component — but the impact on customer experience is significant. Customers who can see "Investigating: API elevated error rates" while they're experiencing errors feel informed rather than abandoned. AzMonitor provides a status API and embeddable widgets that make this integration available without building it yourself, giving you the customer transparency benefits without the engineering overhead.

Tags:status page embedstatus widgetstatus APIin-app status
Back to blog
A
AzMonitor Team
The AzMonitor team writes guides based on experience monitoring millions of endpoints daily across 10,000+ customer environments. Our expertise covers uptime monitoring, SRE practices, and reliability engineering.
Try AzMonitor free

3 monitors free forever · No credit card needed · Set up in 2 minutes

Start monitoring free →