Stencil compiles your components into standards-based custom elements that run natively in the browser — no framework runtime, lazy-loaded chunks, and full TypeScript support out of the box. But custom elements shipping fast to users still doesn't protect you from server outages, broken APIs, or silently failing background jobs. Vigilmon gives you the production observability your Stencil app needs: continuous HTTP monitoring, heartbeat checks, and instant alerts routed to email or Slack.
What You'll Build
- A health API endpoint for your Stencil application backend
- Vigilmon HTTP monitors for your Stencil component library host and backend API
- A heartbeat for scheduled background tasks
- Alert channels (email and Slack)
- An uptime badge as a Stencil web component
Prerequisites
- A Stencil project (created with
npm init stencil) - Node.js backend for the health endpoint
- A free Vigilmon account
Step 1: Add a Health Endpoint
Stencil components are client-side custom elements; your backend exposes data APIs and serves the component bundles. Add a /health route:
// server.js (Express)
import express from "express";
const app = express();
app.get("/health", async (req, res) => {
const checks = {};
let degraded = false;
// Database check
try {
// await db.query("SELECT 1");
checks.database = "ok";
} catch (err) {
checks.database = `error: ${err.message}`;
degraded = true;
}
// External API check
try {
const response = await fetch("https://api.example.com/ping", {
signal: AbortSignal.timeout(3000),
});
checks.external_api = response.ok ? "ok" : `http_${response.status}`;
} catch (err) {
checks.external_api = "unreachable";
degraded = true;
}
res.status(degraded ? 503 : 200).json({
status: degraded ? "degraded" : "ok",
timestamp: new Date().toISOString(),
checks,
});
});
app.listen(process.env.PORT ?? 3000);
Test it:
curl http://localhost:3000/health | jq .
# {"status":"ok","timestamp":"2026-07-03T...","checks":{"database":"ok","external_api":"ok"}}
Step 2: Configure Vigilmon HTTP Monitors
Log in to Vigilmon → Monitors → New Monitor:
Monitor 1: Stencil App Host (component entry page)
| Field | Value |
|---|---|
| URL | https://yourapp.com/ |
| Method | GET |
| Expected status | 200 |
| Expected body | A stable tag from your HTML (e.g. your root custom element <my-app>) |
| Check interval | 1 minute |
Monitor 2: Backend Health API
| Field | Value |
|---|---|
| URL | https://yourapp.com/health |
| Method | GET |
| Expected status | 200 |
| Check interval | 1 minute |
Two monitors catch different failure classes: the CDN/static layer serving your custom element bundles, and the backend data layer your components depend on.
Step 3: Heartbeat for Background Tasks
Stencil apps often consume data from scheduled backend pipelines — content indexing, cache hydration, push notification batches. Silent failures there break your components' data without any visible error.
npm install node-cron
// scheduler.js
import cron from "node-cron";
const VIGILMON_HEARTBEAT_URL = process.env.VIGILMON_HEARTBEAT_URL ?? "";
async function pingHeartbeat() {
if (!VIGILMON_HEARTBEAT_URL) return;
try {
await fetch(VIGILMON_HEARTBEAT_URL, { signal: AbortSignal.timeout(5000) });
} catch {
// Monitoring must never crash the job
}
}
cron.schedule("*/5 * * * *", async () => {
try {
await hydrateComponentData();
await pingHeartbeat();
} catch (err) {
console.error("[scheduler] Data hydration failed:", err);
// Vigilmon alerts after the heartbeat window expires with no ping
}
});
async function hydrateComponentData() {
// Pre-fetch and cache data for Stencil components
}
Set up the heartbeat in Vigilmon:
- Monitors → New Heartbeat Monitor
- Set Expected interval to 10 minutes (2× the cron interval)
- Copy the ping URL to your
.env:
VIGILMON_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/xxxxxxxx
Step 4: Build an Uptime Badge as a Stencil Component
Stencil's component model is ideal for an encapsulated status badge you can drop into any host:
// src/components/vigilmon-badge/vigilmon-badge.tsx
import { Component, Prop, State, h } from "@stencil/core";
@Component({
tag: "vigilmon-badge",
shadow: true,
styles: `
:host { display: inline-flex; align-items: center; }
img { transition: opacity 0.2s; }
`,
})
export class VigilmonBadge {
@Prop() monitorId!: string;
@Prop() monitorSlug!: string;
@State() loaded = false;
private get badgeUrl() {
return `https://vigilmon.online/api/badge/${this.monitorId}.svg`;
}
private get statusUrl() {
return `https://vigilmon.online/status/${this.monitorSlug}`;
}
render() {
return (
<a
href={this.statusUrl}
target="_blank"
rel="noopener noreferrer"
aria-label="Service uptime status"
>
<img
src={this.badgeUrl}
alt="Uptime"
width={120}
height={20}
style={{ opacity: this.loaded ? "1" : "0" }}
onLoad={() => (this.loaded = true)}
/>
</a>
);
}
}
Use the component anywhere in your HTML:
<!-- index.html -->
<script type="module" src="/build/myapp.esm.js"></script>
<footer>
<vigilmon-badge
monitor-id="your-monitor-id"
monitor-slug="your-monitor-slug"
></vigilmon-badge>
</footer>
Because the badge is a true custom element, it works in any framework or plain HTML page without modification.
Step 5: Set Up Alert Channels
In Vigilmon's Alert Channels settings:
- Alert Channels → Email → add your on-call email
- Attach to both HTTP monitors and the heartbeat
Slack
- Create a Slack Incoming Webhook for
#alerts - Alert Channels → Webhook → paste the URL
- Payload template:
{
"text": "🚨 *{{ monitor.name }}* is DOWN\n{{ monitor.url }}\nStatus: {{ event.status }}\n<https://vigilmon.online|Open Vigilmon>"
}
Step 6: Verify Everything Works
# 1. Health endpoint
curl -s https://yourapp.com/health | jq .
# 2. Break the DB connection string → confirm health returns 503
# Expected: Vigilmon alerts within 2 minutes
# 3. Remove VIGILMON_HEARTBEAT_URL → let the scheduler run without pinging
# Expected: heartbeat alert fires after the grace window
# 4. Vigilmon → "Test Alert" → confirm delivery to Slack/email
Summary
| Monitor | What It Catches | |---|---| | HTTP app host check | CDN failures, broken static bundle deploys | | HTTP health API | Database, cache, and third-party API failures | | Heartbeat | Scheduler crashes, silent data hydration failures |
Next Steps
- Create separate monitors for staging vs. production
- Use Vigilmon's response time graphs to detect API latency spikes affecting your Stencil lazy-loaded components
- Configure multi-channel alerting: email for low-severity, Slack for critical
- Publish your
<vigilmon-badge>component to npm so other teams can reuse it across host applications
Ready to get started? Sign up for Vigilmon free — have your first monitor running in under 5 minutes.