ASP.NET

Auto-refresh an ASP.NET page every 10 seconds

ASP.NET · · Published 2026-10-04

Dashboards, queue monitors and score boards need fresh data without the user pressing F5. Reloading the whole page every ten seconds takes one line; refreshing only the part that changes is smoother and lighter, and Web Forms has a control for exactly that.

Whole page: Refresh header or meta tag

protected void Page_Load(object sender, EventArgs e)
{
    Response.AppendHeader("Refresh", "10"); // reload this URL every 10 seconds
}

Or in the markup: <meta http-equiv="refresh" content="10">. Each reload is a fresh GET: the browser re-requests the page, scroll position may reset, and anything typed in a form is lost. It suits wall displays, not pages people interact with.

Part of the page: Timer and UpdatePanel

<asp:ScriptManager ID="sm" runat="server" />

<asp:UpdatePanel ID="upStatus" runat="server" UpdateMode="Conditional">
    <ContentTemplate>
        <asp:Timer ID="tmrRefresh" runat="server" Interval="10000" OnTick="tmrRefresh_Tick" />
        <p>Orders waiting: <asp:Label ID="lblWaiting" runat="server" /></p>
        <p class="small">Updated <asp:Label ID="lblTime" runat="server" /></p>
    </ContentTemplate>
</asp:UpdatePanel>
protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack) LoadStatus();
}

protected void tmrRefresh_Tick(object sender, EventArgs e)
{
    LoadStatus();
}

private void LoadStatus()
{
    lblWaiting.Text = OrderQueue.CountWaiting().ToString();
    lblTime.Text = DateTime.Now.ToString("HH:mm:ss");
}

The Timer control's Interval is in milliseconds. Each tick performs an asynchronous postback; because the timer sits inside the panel, only that panel is re-rendered. The rest of the page, including form fields being edited, is untouched.

To refresh a panel from a timer placed elsewhere, register it as a trigger: <Triggers><asp:AsyncPostBackTrigger ControlID="tmrRefresh" EventName="Tick" /></Triggers>. Set tmrRefresh.Enabled = false from code to stop refreshing, for example when a job has finished.

Keep in mind that a partial postback still sends the full form and view state and runs the full page life cycle on the server. On a heavy page, ten-second ticks from many users add up.

A lighter alternative: fetch JSON

When only a number or two change, ask the server for those values alone. A generic handler (Status.ashx) returns JSON:

public class Status : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "application/json";
        context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        context.Response.Write("{\"waiting\":" + OrderQueue.CountWaiting() + "}");
    }
    public bool IsReusable => true;
}
async function refreshStatus() {
  try {
    const res = await fetch("Status.ashx", { cache: "no-store" });
    if (res.ok) {
      const data = await res.json();
      document.getElementById("waiting").textContent = data.waiting;
    }
  } catch (e) { /* network hiccup: try again next time */ }
}
setInterval(refreshStatus, 10000);

Pause polling when the tab is hidden by checking document.visibilityState in the callback; nobody needs updates for a page they are not looking at.

Accessibility

Content that changes on its own can be missed by screen-reader users or disrupt them. Wrap the changing value in a container with aria-live="polite" so updates are announced without interrupting, and provide a way to pause automatic updates if they are more than a small status value (WCAG 2.2.2).

To leave the page for another one after a delay instead, see Redirect after a few seconds.

Official documentation