Your server is online. Your website says nothing. The quiet engineering of a live status widget
Your server is online. Your website says nothing. The quiet engineering of a live status widget Here's a pattern I keep meeting: a Minecraft community spends weeks on their server, gets the gameplay tight, the Discord b
Your server is online. Your website says nothing. The quiet engineering of a live status widget
Here's a pattern I keep meeting: a Minecraft community spends weeks on their server, gets the gameplay tight, the Discord busy — and their website is a static poster. No signal about whether anyone is playing right now. Visitors can't tell a dead server from a healthy one, so they assume the worst and bounce.
The fix looks trivial ("add a status badge!") and almost everyone implements it wrong. I recently shipped a server-site template called OBSIDIAN, and the status widget ended up being the part I thought about the hardest. This post is the write-up.
Why most status badges lie
The naive implementation polls the server, and when the poll fails it shows 0 players. That's a lie in both directions:
- API unreachable? The server might be perfectly up. You're showing "0 players online" on a page whose real message is dead.
- Server restarting? "0 players" reads as "dead server" — for the 90 seconds it takes to boot back.
Users don't read "0" as a number. They read it as a verdict.
The honest version needs four states, not two: checking, online, offline, and a separate can't reach the status service state that explicitly refuses to guess. That fourth state is the unpopular one — it admits "I don't know" on your own production page — but it's the only configuration that never lies.
Where the query actually lives
Raw Minecraft status isn't HTTP. Java Edition answers a handshake over TCP; Bedrock speaks RakNet, a UDP protocol with its own packet framing. A static page can't speak either from the browser — no raw TCP/UDP in a JS context, by design.
So the honest architecture is a two-hop: the page calls a public status API that performs the protocol handshake server-side and returns JSON. api.mcsrvstat.us is free, needs no key, and speaks both editions — you give it your address, it resolves Java vs Bedrock and returns player counts, version, MOTD.
const res = await fetch(
"https://api.mcsrvstat.us/2/" + encodeURIComponent(address)
);
const data = await res.json();
// data.online is the only field you trust for state
Everything downstream flows from data.online — never from a player count, never from a catch block.
The state machine I ended up with
check() -> [checking] -> ok? -> [online] show players, version, MOTD
-> no? -> [offline] "Server is offline" (plain, no shame)
network error -> [error] "Can't reach the status service.
The server may still be up."
Two rules make it behave under real traffic:
- Never cache "online" optimistically. If the API says offline, show offline now; visitors respect a page that updates honestly.
- The error state names the failure honestly. "Could not reach the status service" tells the owner to check the API, not their server. Ambiguity here generates support questions about the wrong problem.
One config file, because owners aren't developers
The widget ships inside a site template, so the whole thing is driven by one file (js/config.js): server name, address, Discord invite, staff list. The owner never edits logic. This matters more than any technical choice in the stack — a config typo is a question I can answer in one line; a broken app.js edit is a debugging session neither of us wants.
The design half of the story: the live numbers get the only warm accent color on the page. One lamp in a dark cave. And when the server is offline, the number doesn't turn red — red says emergency, and an offline hobby server isn't one. It dims to a quiet neutral. The page stays honest without yelling.
If you want the whole thing
OBSIDIAN is the full template: status widget, one-click copy-IP bar, responsive layout, and a design doc explaining every decision — pixelforgehq.sell.app/product/obsidian-mc-template, intro-priced at $5.99. The demo runs the widget live against a real server: obsidianmc.vercel.app
But honestly — the four-state pattern costs nothing to copy into your own page. Poll data.online, never trust player counts, refuse to guess when the chain breaks. Your website stops lying, and your players notice.
Indie dev, building in public — more admin tools at pixelforgehq.sell.app.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.