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.
3 monitors free forever · No credit card needed · Set up in 2 minutes
Start monitoring free →