Most Laravel apps don't need websockets, they need a page that quietly refreshes itself every few seconds without a full reload. Inertia.js doesn't ship a real-time layer out of the box, but its router.reload() combined with a plain setInterval covers the majority of "is there new data yet" use cases — a dashboard, an order status, a job queue count — with none of the infrastructure that Echo and a websocket server bring along.
The simplest version: router.reload on an interval
Inertia's router.reload() re-requests the current page's props from the server and merges them into the page component without a full page visit — no URL change, no component remount, no flash of unstyled content. Wrapped in a setInterval inside a onMounted/useEffect hook (depending on whether you're on Vue or React), it gives you polling in about five lines, and because it goes through the normal Inertia request cycle, your existing middleware, authorization, and prop transformation all apply unchanged.
import { router } from '@inertiajs/vue3'
import { onMounted, onUnmounted } from 'vue'
let interval
onMounted(() => {
interval = setInterval(() => {
router.reload({ only: ['orderStatus'] })
}, 5000)
})
onUnmounted(() => clearInterval(interval))
Passing only: ['orderStatus'] tells the server (via the X-Inertia-Partial-Data header) to compute and return just that prop, skipping every other prop's resolution. Without it, a poll every 5 seconds re-runs every query the page depends on, which turns a cheap status check into a full page reload's worth of database work, five times a minute.
Pausing when the tab is hidden
An interval that keeps firing while the browser tab is backgrounded wastes requests and, at scale, wastes server load across every idle tab your users have open. The Page Visibility API (document.visibilityState) is the built-in fix — pause the interval on visibilitychange to hidden, and do one immediate reload plus resume the interval when the tab becomes visible again, so the user doesn't come back to five-second-stale data after tabbing away for ten minutes.
function startPolling(intervalMs = 5000) {
let timer = setInterval(poll, intervalMs)
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
clearInterval(timer)
} else {
poll()
timer = setInterval(poll, intervalMs)
}
})
function poll() {
router.reload({ only: ['orderStatus'], showProgress: false })
}
}
Avoiding overlapping requests on a slow connection
A naive setInterval fires on a fixed schedule regardless of whether the previous request finished, and on a slow connection or a temporarily overloaded server, requests can stack up faster than they resolve. It's more robust to poll with a self-rescheduling setTimeout instead — fire the request, wait for it to settle (success or failure), then schedule the next one — so a slow response naturally backs off the polling rate instead of piling requests into a queue.
If the same polling reload can be triggered from two places (say, a manual refresh button and the interval both firing close together), you can end up with overlapping requests racing to update the same prop. Track an in-flight flag or use Inertia's onBefore/onFinish visit callbacks to skip a scheduled poll if one is already running.
When polling stops being enough
Polling is the right tool when "up to N seconds stale" is an acceptable answer and the number of concurrently polling clients is modest. It stops being the right tool when you need sub-second latency, when the polling interval has to shrink low enough that request overhead dominates, or when the number of open tabs polling your server starts showing up as load in its own right. At that point, Laravel Echo backed by Reverb (or Pusher) with real broadcast events is the correct upgrade — it pushes updates instead of the client asking on a timer, and it's a genuinely different piece of infrastructure, not a tweak to the polling interval.
Wrapping up
For most "is there new data yet" screens, router.reload({ only: [...] }) on a visibility-aware interval gets you 90% of the perceived responsiveness of websockets for a fraction of the infrastructure — no broadcast driver, no persistent connections to manage, no Echo client to keep alive. Reach for Laravel Echo and real broadcasting only once you've measured that polling's staleness window or request volume is an actual problem, not a hypothetical one.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.