Wicked Smart Data
LearnInsightsAboutContact
Sign InLet's Build
LearnInsightsAboutContact
Sign InLet's Build
Wicked Smart Data

Intelligence, automation, and expert execution — plus an elite library of free knowledge. We turn complexity into competitive advantage.

Start a conversation

Platform

  • Learning Paths
  • Insights
  • RSS Feed

Company

  • About
  • Contact
  • Work With Us

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Wicked Smart Data. All rights reserved.

Intelligence · Automation · Advantage

All Insights
Power Apps

Canvas App Timer and Auto-Refresh Patterns: Polling Data Sources, Updating Galleries, and Triggering Background Actions Without User Interaction

Most Canvas App dashboards make users press a refresh button — but production ops boards need to stay current automatically. This deep-dive lesson teaches you three distinct polling architectures, multi-rate timer strategies, and how to trigger Power Automate flows on a schedule, all without a single user click.

⚡ Practitioner23 min readOct 22, 2026Updated Oct 22, 2026
Canvas App Timer and Auto-Refresh Patterns: Polling Data Sources, Updating Galleries, and Triggering Background Actions Without User Interaction
On this page
  • Introduction
  • Prerequisites
  • Understanding the Timer Control's True Behavior
  • The Timer Is Not a Background Process
  • Key Timer Properties
  • The Hidden Property: `Value`
  • Pattern 1: Direct Query Refresh (Simple Galleries)
  • Setting It Up
  • Displaying Last Refresh Time
  • Pattern 2: Collection-Backed Refresh (The Safer Default)
  • Why Collections Change Everything
  • Setting Up the Collection Pattern
  • Keeping User Selections Stable During Refresh
  • Pattern 3: Hybrid Refresh with Delta Detection
  • The Delta Pattern
  • Triggering Power Automate Flows From a Timer
  • Building a Flow That Accepts a Timer Trigger
  • Combining Data Refresh and Flow Trigger in a Single Timer
  • Multi-Rate and Multi-Screen Timer Architecture
  • Strategy 1: Multiple Timer Controls on One Screen
  • Strategy 2: Single Timer with Modulo Counters
  • Pausing Auto-Refresh When the App Isn't in Use
  • Hands-On Exercise: Building a Live Dispatch Dashboard
  • Step 1: Screen and Data Setup
  • Step 2: The Gallery
  • Step 3: The Timer
  • Step 4: Status Bar
  • Step 5: Manual Refresh Button
  • Common Mistakes and Troubleshooting
  • Mistake 1: Timer Fires but Gallery Doesn't Update
  • Mistake 2: Timer Runs On Wrong Screen
  • Mistake 3: OnTimerEnd Formula is Too Slow
  • Mistake 4: Performance Degradation Over Time
  • Mistake 5: Timer Doesn't Start on App Load
  • Performance Considerations and When Not to Use Timers
  • When Timers Work Well
  • When to Consider Alternatives
  • Summary and Next Steps
  • Canvas App Timer and Auto-Refresh Patterns: Polling Data Sources, Updating Galleries, and Triggering Background Actions Without User Interaction

    Introduction

    Imagine you've built a beautiful operations dashboard in Power Apps — a gallery showing open service tickets, a KPI card displaying queue depth, and a status board tracking field technician locations. Your users love it. Then someone asks: "Why do I have to keep pressing the refresh button? The data in SharePoint changes every few minutes." And just like that, you have a new requirement: the app needs to stay current without the user lifting a finger.

    This is the auto-refresh problem, and it comes up constantly in real-world Power Apps projects. Dispatch boards, warehouse queue monitors, manufacturing floor dashboards, IT helpdesk screens — any scenario where data changes on a schedule or based on external events needs some form of polling or background refresh strategy. Power Apps gives you a dedicated control for this: the Timer control. But using it well requires understanding how Canvas Apps process formulas, manage state, and interact with data sources. Done carelessly, timer-driven refresh turns a slick dashboard into a CPU-burning, delegation-violating mess that makes your SharePoint list beg for mercy.

    By the end of this lesson, you'll know how to configure Timer controls precisely, wire them to gallery refreshes and collection updates, build rate-limited polling patterns that don't hammer your data source, trigger Power Automate flows on a schedule without user clicks, and handle all the edge cases — pausing timers when the app isn't visible, resetting counters cleanly, and keeping the UI responsive while background work happens.

    What you'll learn:

    • How the Timer control works internally — its properties, lifecycle, and the formulas that actually execute
    • Three distinct auto-refresh patterns (direct query, collection-backed, and hybrid) with trade-offs for each
    • How to trigger Power Automate flows from a timer without user interaction
    • Techniques for rate-limiting, pause/resume, and multi-interval timing
    • Performance implications and when a timer-based approach is the wrong tool entirely

    Prerequisites

    You should be comfortable building Canvas Apps with galleries, working with variables, and writing intermediate formulas using Filter, Patch, and ClearCollect. If you need to brush up, the lessons on Power Apps formulas including Navigate, Filter, Lookup, and Patch and collections and local data management will fill those gaps quickly. You should also have at least one data source connected — the examples here use SharePoint lists, but the patterns translate directly to Dataverse, SQL Server, and REST APIs.


    Understanding the Timer Control's True Behavior

    Before you wire anything up, you need a clear mental model of what the Timer control actually does — because it doesn't behave the way most people initially assume.

    The Timer Is Not a Background Process

    The first misconception: people expect the Timer to run silently in the background, independent of the UI. It doesn't. The Timer control is a UI control, just like a button or a label. It lives on a screen. When that screen is active and the user is looking at it, the timer ticks. When the user navigates away, the timer on the previous screen effectively stops influencing the UI, even if technically it continues in the runtime.

    This matters because if you place your Timer on Screen1 and navigate to Screen2, the timer's OnTimerEnd formula won't fire in any meaningful way for the user on Screen2. Your auto-refresh logic is therefore inherently screen-scoped unless you take deliberate steps to work around this — which we'll cover in the multi-screen pattern section.

    Key Timer Properties

    Open a blank canvas app, insert a Timer control (Insert → Input → Timer), and explore these properties:

    Duration — the length of one cycle in milliseconds. A 30-second refresh means Duration = 30000. This is not a countdown to some future event; it's the length of one complete tick cycle.

    Repeat — when set to true, the timer automatically restarts after completing one cycle. This is what makes continuous polling work. When false, the timer fires once and stops.

    AutoStart — when true, the timer begins ticking as soon as the screen loads, without waiting for a user action. This is usually what you want for dashboard auto-refresh.

    OnTimerEnd — this is the formula that executes when the timer completes one full cycle. This is where your refresh logic lives. Think of it as the timer's equivalent of a button's OnSelect.

    OnTimerStart — fires at the beginning of each cycle. Less commonly used, but handy for setting a "last refresh started" timestamp or showing a loading indicator.

    OnSelect — fires when the user clicks the timer control. Since you'll usually hide the timer from the user (set Visible = false), this rarely matters.

    Start — a boolean that controls whether the timer is running. You can bind this to a variable to programmatically pause and resume the timer. When Start = false, the timer is fully paused.

    Note

    The Duration and Repeat properties behave slightly differently than you might expect. If Repeat = true and Duration = 30000, the OnTimerEnd formula fires every 30 seconds from the moment the timer starts. Each cycle is independent — the timer doesn't drift based on how long your OnTimerEnd formula takes to execute. However, if your OnTimerEnd formula takes 8 seconds to complete (say, a slow network call), the next cycle still starts 30 seconds after the previous one started, not after the formula finishes. Keep your refresh formulas fast.

    The Hidden Property: `Value`

    The Timer.Value property returns the current elapsed time in milliseconds within the current cycle. This is useful for building progress indicators — a "refreshing in X seconds" countdown — but it doesn't cause the timer to re-trigger. It's purely for display purposes.


    Pattern 1: Direct Query Refresh (Simple Galleries)

    The simplest pattern: when the timer fires, re-query the data source and let the gallery update automatically. This works when your gallery's Items property points directly to a data source expression.

    Setting It Up

    Assume you have a gallery called GalleryOpenTickets with its Items property set to:

    Filter(
        ServiceTickets,
        Status = "Open",
        AssignedTo = varCurrentUser
    )
    

    Where ServiceTickets is your SharePoint list connector. This is a live query — every time Power Apps evaluates this formula, it fetches fresh data from SharePoint.

    The trick is that Power Apps is lazy about re-evaluation. It doesn't re-run your gallery's Items formula on a schedule. It re-runs it when something in the formula changes — a variable, a control value, or a data source signal.

    To force a re-evaluation, you introduce a "refresh token" — a variable that your timer updates, and that your gallery's Items formula references:

    Timer properties:

    Duration: 30000
    Repeat: true
    AutoStart: true
    Visible: false
    

    Timer OnTimerStart:

    Set(varLastRefreshTime, Now())
    

    Timer OnTimerEnd:

    Set(varRefreshTick, varRefreshTick + 1)
    

    Gallery Items property:

    Filter(
        ServiceTickets,
        Status = "Open",
        AssignedTo = varCurrentUser,
        varRefreshTick >= 0
    )
    

    The varRefreshTick >= 0 predicate is always true, but its presence in the formula means Power Apps watches varRefreshTick for changes. When the timer fires and increments the variable, Power Apps detects the dependency change and re-evaluates the entire Items formula — triggering a fresh data fetch.

    Tip

    Initialize varRefreshTick in your App's OnStart or screen's OnVisible with Set(varRefreshTick, 0). Without initialization, the first evaluation of varRefreshTick + 1 will treat the variable as blank, which Power Apps coerces to 0, so you'll get 1 on the first tick. That actually works fine, but being explicit is better practice.

    Displaying Last Refresh Time

    Add a label to the screen with this Text property:

    "Last updated: " & Text(varLastRefreshTime, "[$-en-US]h:mm:ss AM/PM")
    

    This uses varLastRefreshTime (set in OnTimerStart) to show users when the data last refreshed. It's a small UX detail that dramatically reduces confusion — users see the clock ticking forward and know the app is alive.

    Warning

    Direct query refresh is fast to implement but delegates every query cycle to your data source. If you have 20 users with 30-second refresh intervals on a SharePoint list, that's 40 reads per minute. For small teams this is fine. For 200 users on a slow data source like an Excel file in OneDrive, you'll create serious performance problems. Think about your audience before choosing this pattern.


    Pattern 2: Collection-Backed Refresh (The Safer Default)

    Instead of querying the data source live on every gallery render, this pattern loads data into an in-memory collection on each timer tick, and the gallery reads from the collection. The user sees instantaneous gallery interactions (filtering, scrolling) because all data is local — only the background refresh hits the network.

    Why Collections Change Everything

    When GalleryOpenTickets.Items points to a Filter() on a SharePoint connector, every scroll, search, or selection might trigger a delegated query. With a collection, all of that happens in memory. You pay the network cost once per timer interval, not on every user interaction. This is also why you'll see advanced gallery architecture patterns favor collections for high-interactivity screens.

    Setting Up the Collection Pattern

    App OnStart (or screen OnVisible):

    ClearCollect(
        colOpenTickets,
        Filter(
            ServiceTickets,
            Status = "Open",
            AssignedTo = varCurrentUser
        )
    );
    Set(varLastRefreshed, Now());
    Set(varIsRefreshing, false)
    

    Timer OnTimerStart:

    Set(varIsRefreshing, true)
    

    Timer OnTimerEnd:

    ClearCollect(
        colOpenTickets,
        Filter(
            ServiceTickets,
            Status = "Open",
            AssignedTo = varCurrentUser
        )
    );
    Set(varLastRefreshed, Now());
    Set(varIsRefreshing, false)
    

    Gallery Items:

    colOpenTickets
    

    No refresh token tricks needed here — when ClearCollect replaces the collection's contents, Power Apps automatically re-renders any gallery bound to it.

    Loading indicator — add a spinner icon or loading label with:

    Visible: varIsRefreshing
    

    This gives users visual feedback that a refresh is in progress, which matters when the SharePoint query takes 2-3 seconds.

    Keeping User Selections Stable During Refresh

    One frustrating gotcha: if a user has selected a gallery item and the collection refreshes, the selected item might shift or disappear. Handle this by saving the selected record's ID before refresh and restoring the selection after:

    Timer OnTimerStart:

    Set(varSelectedTicketID, GalleryOpenTickets.Selected.ID);
    Set(varIsRefreshing, true)
    

    Timer OnTimerEnd (expanded):

    ClearCollect(
        colOpenTickets,
        Filter(
            ServiceTickets,
            Status = "Open",
            AssignedTo = varCurrentUser
        )
    );
    Set(varLastRefreshed, Now());
    Set(varIsRefreshing, false);
    // Restore selection if the record still exists
    If(
        !IsBlank(varSelectedTicketID),
        Set(
            varRestoredRecord,
            LookUp(colOpenTickets, ID = varSelectedTicketID)
        )
    )
    

    Then use varRestoredRecord in your detail panel instead of GalleryOpenTickets.Selected. The detail panel continues showing the correct record even if the gallery momentarily clears.

    Key insight

    The collection pattern is almost always better than direct query refresh for non-trivial apps. The only downside is that your data is as stale as your polling interval — but that's inherent to polling. The upside is dramatically smoother user experience and lower connector call frequency per user interaction.


    Pattern 3: Hybrid Refresh with Delta Detection

    For power users: this pattern tracks what's changed between refresh cycles and updates only the affected records. It reduces unnecessary UI churn and can highlight new or changed records visually.

    The Delta Pattern

    The idea is to compare the freshly fetched data against what's already in your collection, identify new records and removed records, and update selectively rather than doing a full ClearCollect.

    Timer OnTimerEnd:

    // Fetch the latest data into a temp collection
    ClearCollect(
        colTicketsFresh,
        Filter(ServiceTickets, Status = "Open", AssignedTo = varCurrentUser)
    );
    
    // Find new records (in fresh but not in current)
    ClearCollect(
        colNewTickets,
        Filter(colTicketsFresh, !(ID in colOpenTickets.ID))
    );
    
    // Find removed records (in current but not in fresh)
    ClearCollect(
        colRemovedTickets,
        Filter(colOpenTickets, !(ID in colTicketsFresh.ID))
    );
    
    // Remove stale records
    RemoveIf(colOpenTickets, ID in colRemovedTickets.ID);
    
    // Add new records
    Collect(colOpenTickets, colNewTickets);
    
    // Update changed records
    ForAll(
        colTicketsFresh,
        Patch(
            colOpenTickets,
            LookUp(colOpenTickets, ID = colTicketsFresh[@ID]),
            colTicketsFresh
        )
    );
    
    // Highlight new records for 5 seconds
    Set(varNewTicketIDs, colNewTickets);
    Set(varLastRefreshed, Now())
    

    Then in your gallery template, highlight new records:

    // Gallery template Fill color
    If(
        GalleryOpenTickets.ThisItem.ID in varNewTicketIDs.ID,
        RGBA(255, 250, 205, 1),  // soft yellow for new items
        RGBA(255, 255, 255, 1)   // white for existing
    )
    

    Warning

    The delta pattern using ForAll with Patch on collections is significantly more CPU-intensive than a simple ClearCollect. For collections under 200 records it's usually fine. For larger datasets, the overhead of the ForAll + LookUp combination can cause visible jank. Profile it in the Monitor tool — see the debugging and monitoring guide for how to measure formula execution time. If it's taking more than 500ms, fall back to the simpler collection pattern.


    Triggering Power Automate Flows From a Timer

    Sometimes you don't want to refresh data — you want to do something on a schedule. Send a reminder, check a threshold and create an alert record, ping an API, or kick off a downstream process. This is where you connect your Timer to a Power Automate flow.

    Building a Flow That Accepts a Timer Trigger

    The flow needs to be an Instant Cloud Flow with the "Power Apps" trigger — this is what allows Canvas Apps to call it with Run(). For a background check scenario, let's say you want to check if any open tickets have exceeded their SLA (been open more than 4 hours) and update a SharePoint field SLABreached = true.

    Power Automate flow structure:

    1. Trigger: Power Apps (v2)
    2. Action: Get Items from ServiceTickets list where Status = "Open" and SLABreached = false
    3. Apply to each: check if Created is more than 4 hours ago
    4. If yes: Update Item — set SLABreached = true, SLABreachTime = utcNow()

    No inputs needed from the canvas app for this flow — the flow handles everything internally.

    Timer OnTimerEnd (30-minute interval for SLA checks):

    // Only run the SLA check if the user has been on this screen
    If(
        varScreenActiveSeconds > 60,  // don't run immediately on screen load
        SLABreachCheckFlow.Run();
        Set(varLastSLACheck, Now())
    )
    

    Note

    Canvas App flows run in the context of the signed-in user. If your flow updates SharePoint items, the user needs write permissions on that list. If you need the flow to run with elevated permissions, you'll need to configure connection references carefully — or use a different trigger altogether. See the Power Apps security and permissions guide for the nuances of connection sharing.

    Combining Data Refresh and Flow Trigger in a Single Timer

    You can absolutely do both in one OnTimerEnd block:

    // 1. Refresh the gallery collection
    ClearCollect(
        colOpenTickets,
        Filter(ServiceTickets, Status = "Open", AssignedTo = varCurrentUser)
    );
    
    // 2. Check if enough time has passed to run the heavy flow
    // (Only every 10th tick if your timer is 30s, so every 5 minutes)
    Set(varTickCount, varTickCount + 1);
    If(
        Mod(varTickCount, 10) = 0,
        SLABreachCheckFlow.Run()
    );
    
    Set(varLastRefreshed, Now())
    

    This technique — using a modulo counter — lets a single timer drive multiple refresh intervals. Your gallery refreshes every 30 seconds, your expensive flow runs every 5 minutes. It's clean and avoids the complexity of managing multiple timer controls.


    Multi-Rate and Multi-Screen Timer Architecture

    Real dashboards often need different refresh rates for different data. Queue counts might need refreshing every 15 seconds. Summary KPIs every 2 minutes. Audit logs every 10 minutes. You have two strategies.

    Strategy 1: Multiple Timer Controls on One Screen

    Place three invisible Timer controls on the dashboard screen, each with a different Duration. Each fires its own OnTimerEnd. Simple, explicit, easy to read.

    // Timer_Fast (15 seconds) - OnTimerEnd
    ClearCollect(colQueueCounts, QueueSummary);
    Set(varQueueRefreshed, Now())
    
    // Timer_Medium (120 seconds) - OnTimerEnd
    ClearCollect(colKPIs, KPIMetrics);
    Set(varKPIRefreshed, Now())
    
    // Timer_Slow (600 seconds) - OnTimerEnd
    ClearCollect(colAuditSummary, Filter(AuditLog, Created > DateAdd(Now(), -24, Hours)));
    Set(varAuditRefreshed, Now())
    

    The downside: if you have five or six different refresh rates, you end up with five or six timers, which is visually cluttered in the canvas editor and conceptually complex.

    Strategy 2: Single Timer with Modulo Counters

    One timer at the greatest-common-divisor interval, with counters tracking how many ticks have passed since each "slow" action last ran:

    // Single timer at 15 seconds
    // OnTimerEnd:
    
    // Always refresh queue counts
    ClearCollect(colQueueCounts, QueueSummary);
    
    // Refresh KPIs every 8 ticks (8 × 15s = 2 minutes)
    Set(varTickCount, varTickCount + 1);
    If(Mod(varTickCount, 8) = 0,
        ClearCollect(colKPIs, KPIMetrics)
    );
    
    // Refresh audit log every 40 ticks (40 × 15s = 10 minutes)
    If(Mod(varTickCount, 40) = 0,
        ClearCollect(colAuditSummary, Filter(AuditLog, Created > DateAdd(Now(), -24, Hours)))
    );
    
    Set(varLastRefreshed, Now())
    

    This is elegant when the intervals have clean relationships. It's harder to reason about when intervals don't divide evenly — in that case, multiple timers with explicit durations are clearer.

    Pausing Auto-Refresh When the App Isn't in Use

    Nobody wants their app hammering a SharePoint list while it's sitting idle on a locked screen. Implement a "user activity" detector using global variable updates tied to real interactions:

    On every significant user interaction (button taps, gallery selections, text input changes):

    Set(varLastUserActivity, Now())
    

    Timer's Start property:

    DateDiff(varLastUserActivity, Now(), TimeUnit.Minutes) < 15
    

    When the user hasn't interacted for 15 minutes, the timer stops. When they tap anything, varLastUserActivity updates, the Start condition becomes true again, and polling resumes automatically.

    Tip

    You can also display a "Refresh paused — tap anywhere to resume" banner when the timer is stopped. Set the banner's Visible to !TimerRefresh.Running (or whatever you've named the control). This reassures users who come back to the screen after a while that they might be looking at stale data.


    Hands-On Exercise: Building a Live Dispatch Dashboard

    Let's build this properly. You're creating a dispatch board for a logistics company. Field technicians update their job status in a SharePoint list called FieldJobs with columns: Title, TechnicianName, Status (Pending / In Progress / Completed), Priority (High / Medium / Low), LastUpdated.

    The dashboard needs to show all in-progress jobs, refresh every 45 seconds, highlight jobs that haven't been updated in over an hour (potentially stalled), and run an escalation check every 5 minutes.

    Step 1: Screen and Data Setup

    Create a new screen called ScreenDispatch. In the screen's OnVisible:

    Set(varCurrentUser, User().FullName);
    Set(varIsRefreshing, false);
    Set(varTickCount, 0);
    Set(varLastUserActivity, Now());
    ClearCollect(
        colActiveJobs,
        Filter(FieldJobs, Status = "In Progress")
    );
    Set(varLastRefreshed, Now())
    

    Step 2: The Gallery

    Insert a gallery named GalleryActiveJobs. Set:

    • Items: SortByColumns(colActiveJobs, "Priority", SortOrder.Ascending, "LastUpdated", SortOrder.Ascending)
    • Template height: 80px

    Inside the template, add:

    • A label for ThisItem.Title
    • A label for ThisItem.TechnicianName
    • A label for ThisItem.Status
    • A colored rectangle indicating stall status

    Stall indicator rectangle Fill:

    If(
        DateDiff(ThisItem.LastUpdated, Now(), TimeUnit.Hours) >= 1,
        RGBA(255, 100, 100, 0.8),   // red - stalled
        RGBA(100, 200, 100, 0.8)    // green - active
    )
    

    Step 3: The Timer

    Insert a Timer control named TimerDispatchRefresh. Set:

    Duration: 45000
    Repeat: true
    AutoStart: true
    Visible: false
    Start: DateDiff(varLastUserActivity, Now(), TimeUnit.Minutes) < 20
    

    OnTimerStart:

    Set(varIsRefreshing, true);
    Set(varSelectedJobID, GalleryActiveJobs.Selected.ID)
    

    OnTimerEnd:

    ClearCollect(
        colActiveJobs,
        Filter(FieldJobs, Status = "In Progress")
    );
    Set(varLastRefreshed, Now());
    Set(varIsRefreshing, false);
    
    // Escalation check every 5 minutes (every ~7 ticks at 45s)
    Set(varTickCount, varTickCount + 1);
    If(
        Mod(varTickCount, 7) = 0,
        EscalationCheckFlow.Run()
    );
    
    // Track restored selection
    If(
        !IsBlank(varSelectedJobID),
        Set(varRestoredJob, LookUp(colActiveJobs, ID = varSelectedJobID))
    )
    

    Step 4: Status Bar

    Add a horizontal container at the bottom of the screen with three elements:

    Refreshing indicator (spinner or label):

    Text: If(varIsRefreshing, "Refreshing...", "Live")
    Color: If(varIsRefreshing, RGBA(200, 150, 0, 1), RGBA(0, 150, 0, 1))
    

    Last updated label:

    Text: "Updated: " & Text(varLastRefreshed, "[$-en-US]h:mm:ss AM/PM")
    

    Paused indicator:

    Text: "Auto-refresh paused"
    Visible: !TimerDispatchRefresh.Running
    

    Step 5: Manual Refresh Button

    Always give users a manual refresh option — some will be impatient and some will want to confirm a specific state:

    Button OnSelect:

    Set(varIsRefreshing, true);
    Set(varLastUserActivity, Now());  // Reset inactivity timer
    ClearCollect(
        colActiveJobs,
        Filter(FieldJobs, Status = "In Progress")
    );
    Set(varLastRefreshed, Now());
    Set(varIsRefreshing, false)
    

    Common Mistakes and Troubleshooting

    Mistake 1: Timer Fires but Gallery Doesn't Update

    Symptom: You can see in the Monitor tool that OnTimerEnd runs, but the gallery looks unchanged.

    Cause: The gallery's Items property doesn't reference any variable that changed. The collection name is the same (colOpenTickets), so Power Apps doesn't detect a dependency change — even if the collection's contents changed.

    Fix: This actually shouldn't happen with ClearCollect — Power Apps does detect collection mutations. If you're seeing stale data, check whether you're accidentally referencing the original data source in the gallery instead of the collection. Open the gallery, click Items in the formula bar, and confirm it reads colOpenTickets not ServiceTickets.

    Mistake 2: Timer Runs On Wrong Screen

    Symptom: The timer fires even when the user navigates away, causing collection updates for a screen the user isn't looking at.

    Cause: AutoStart = true and Repeat = true with no guard condition.

    Fix: Add a screen visibility guard. In the timer's Start property:

    Self.Parent = App.ActiveScreen
    

    Wait — Canvas Apps don't expose App.ActiveScreen in a bindable way for controls. The actual fix is to use a global variable toggled by the screen's OnVisible and OnHidden:

    // ScreenDispatch OnVisible
    Set(varDispatchScreenActive, true)
    
    // ScreenDispatch OnHidden
    Set(varDispatchScreenActive, false)
    
    // Timer Start property
    varDispatchScreenActive
    

    Mistake 3: OnTimerEnd Formula is Too Slow

    Symptom: The timer fires every 30 seconds, but the ClearCollect takes 10-12 seconds. The next cycle starts before the previous one finishes, creating overlapping requests.

    Cause: Timer cycles are time-based, not completion-based. If Duration = 30000 and your formula takes 12 seconds, you have 18 seconds of "breathing room."

    Fix 1: Increase the duration to give adequate headroom — at least 3x your average query time.

    Fix 2: Use the varIsRefreshing flag to skip a cycle if one is already running:

    // Timer OnTimerEnd
    If(
        !varIsRefreshing,
        Set(varIsRefreshing, true);
        ClearCollect(colActiveJobs, Filter(FieldJobs, Status = "In Progress"));
        Set(varIsRefreshing, false);
        Set(varLastRefreshed, Now())
    )
    

    Mistake 4: Performance Degradation Over Time

    Symptom: The app runs fine initially but becomes sluggish after 20-30 minutes.

    Cause: If your OnTimerEnd formula uses Collect (rather than ClearCollect) repeatedly, the collection grows unbounded. Each cycle adds records without removing old ones.

    Fix: Always use ClearCollect for polling refresh. It replaces the entire collection atomically. Only use Collect when you intentionally want to append (like logging timer events for debugging).

    Mistake 5: Timer Doesn't Start on App Load

    Symptom: AutoStart = true but the timer doesn't seem to be ticking.

    Cause: Often the Start property has a binding that evaluates to false at startup before the relevant variables are initialized. For example, Start: varDispatchScreenActive will be false (blank) until OnVisible sets it.

    Fix: Confirm variable initialization order. Set variables in App.OnStart before navigating to the first screen, or change the Start property to handle the uninitialized case: Start: varDispatchScreenActive = true Or IsBlank(varDispatchScreenActive).

    Key insight

    Use the Power Apps Monitor tool to verify timer behavior. Filter by "Timer" events and you'll see exactly when OnTimerStart and OnTimerEnd fire, what formulas executed, and how long they took. This is far more reliable than guessing from visual behavior. The debugging guide covers the Monitor tool in depth if you haven't used it yet.


    Performance Considerations and When Not to Use Timers

    Timers are powerful but they have a cost. Here's how to think about whether they're the right tool.

    When Timers Work Well

    • Dashboard screens with 1-50 concurrent users
    • Data that changes on a human timescale (minutes, not sub-second)
    • SharePoint lists, Dataverse tables, or SQL Server with adequate capacity
    • Scenarios where "eventually consistent" data (30-90 second staleness) is acceptable

    When to Consider Alternatives

    SignalR / Real-time push: If you genuinely need sub-10-second data freshness and have more than a handful of users, polling is the wrong model. Look into real-time collaboration patterns with SignalR for event-driven refresh instead of time-driven refresh.

    Power BI embedded: For read-only dashboard scenarios, Power BI has native auto-refresh and far more efficient data handling than Canvas App galleries. Don't use Canvas Apps purely for visualization when Power BI already does it better.

    Delegation and large datasets: If your Filter in OnTimerEnd isn't delegable, each poll fetches the first 500 (or 2000) records and then filters client-side. This means your gallery only ever shows a subset of the actual data — and it also means a heavy payload on every tick. Solve the delegation problem first. The delegation deep dive is essential reading before you put a timer on any screen with non-trivial data volumes.

    Connector rate limits: Some connectors have strict rate limits. SharePoint allows roughly 600 requests per minute per site collection, spread across all users and all apps. If you have 50 users with 15-second refresh intervals, that's 200 requests per minute just from your app. Check your connector's throttling limits and design your polling interval accordingly. The performance optimization guide has more on thinking through connector load.

    Warning

    Never use a timer interval shorter than 10 seconds in a production app used by more than a handful of people. Sub-10-second polling on shared data sources will cause throttling, 429 errors, and a degraded experience for everyone. If you need near-real-time updates, use a push-based architecture instead.


    Summary and Next Steps

    You now have a complete toolkit for timer-based auto-refresh in Canvas Apps. The core patterns:

    1. Direct query refresh — refresh token variable forces gallery re-evaluation. Simple, but every user interaction also queries the data source. Best for low-traffic apps with small datasets.

    2. Collection-backed refresh — ClearCollect on a timer fills a local collection; the gallery reads from memory. Smooth UX, lower per-interaction cost, one network call per cycle. The right default for most production dashboards.

    3. Delta refresh — selective updates to existing collection records with visual highlighting. Best for scenarios where preserving user context (selections, scroll position) during refresh is critical.

    4. Modulo counter multi-rate — single timer driving multiple refresh intervals via tick counting. Keeps architecture clean when different data has different staleness requirements.

    5. Flow triggers from timer — background Power Automate runs without user interaction. Essential for threshold checks, escalations, and any write-side logic that shouldn't wait for a user click.

    The biggest real-world mistakes are poll intervals that are too aggressive, forgetting to guard timers when users navigate away, and skipping the varIsRefreshing protection that prevents overlapping refresh cycles. Build those guards in from the start rather than retrofitting them when users complain.

    For your next step, if your dashboard is showing data from multiple sources (SharePoint for tickets, Dataverse for customer records, a REST API for real-time status), look at connecting Power Apps to SharePoint, Excel, and Dataverse and connecting to REST APIs via custom connectors to make sure your data architecture handles the polling load efficiently. And if you want to add error handling so your dashboard gracefully handles a SharePoint outage without freezing, the error handling patterns guide will walk you through wrapping refresh formulas in IfError blocks that degrade gracefully.

    Work With Us

    From insight to implementation

    Reading is the start. When you're ready to build the data, automation, or AI systems behind it, our team turns strategy into shipped results.

    Let's Build

    Canvas Apps 101

    Previous

    Saving and Submitting Form Data in Canvas Apps: Using SubmitForm, Patch, and Success Notifications to Complete the Data Entry Loop

    Related Insights

    Power AppsFoundation

    Configuring Dataverse Status Reasons and State Transitions: Controlling Record Lifecycle Stages and Valid Status Paths in Model-Driven Apps

    16 min
    Power AppsFoundation

    Saving and Submitting Form Data in Canvas Apps: Using SubmitForm, Patch, and Success Notifications to Complete the Data Entry Loop

    16 min
    Power AppsExpert

    Configuring Dataverse Row-Level Access Teams and Access Team Templates: Dynamically Granting Per-Record Permissions Without Custom Security Roles in Model-Driven Apps

    33 min

    On this page

    • Introduction
    • Prerequisites
    • Understanding the Timer Control's True Behavior
    • The Timer Is Not a Background Process
    • Key Timer Properties
    • The Hidden Property: `Value`
    • Pattern 1: Direct Query Refresh (Simple Galleries)
    • Setting It Up
    • Displaying Last Refresh Time
    • Pattern 2: Collection-Backed Refresh (The Safer Default)
    • Why Collections Change Everything
    • Setting Up the Collection Pattern
    • Keeping User Selections Stable During Refresh
    • Pattern 3: Hybrid Refresh with Delta Detection
    • The Delta Pattern
    • Triggering Power Automate Flows From a Timer
    • Building a Flow That Accepts a Timer Trigger
    • Combining Data Refresh and Flow Trigger in a Single Timer
    • Multi-Rate and Multi-Screen Timer Architecture
    • Strategy 1: Multiple Timer Controls on One Screen
    • Strategy 2: Single Timer with Modulo Counters
    • Pausing Auto-Refresh When the App Isn't in Use
    • Hands-On Exercise: Building a Live Dispatch Dashboard
    • Step 1: Screen and Data Setup
    • Step 2: The Gallery
    • Step 3: The Timer
    • Step 4: Status Bar
    • Step 5: Manual Refresh Button
    • Common Mistakes and Troubleshooting
    • Mistake 1: Timer Fires but Gallery Doesn't Update
    • Mistake 2: Timer Runs On Wrong Screen
    • Mistake 3: OnTimerEnd Formula is Too Slow
    • Mistake 4: Performance Degradation Over Time
    • Mistake 5: Timer Doesn't Start on App Load
    • Performance Considerations and When Not to Use Timers
    • When Timers Work Well
    • When to Consider Alternatives
    • Summary and Next Steps