Our field technicians were reverting to paper notes because their inspection app couldn’t retrieve equipment details in areas with no signal. After we added offline support with SaveData and LoadData, they stopped losing work to dead zones and stopped re-entering the same inspection twice because the first attempt got lost. This article shows the same pattern: how to build a Canvas app that keeps working when the connection doesn’t, using the Power Apps functions SaveData and LoadData.

Why Offline Capabilities Matter

Offline functionality lets users:

  • Keep working uninterrupted when connections drop
  • Pre-load critical reference data before venturing into low-connectivity areas
  • Capture and store data locally to sync later
  • Eliminate the frustration of lost work due to network issues

SaveData and LoadData: Core Offline Functions

SaveData writes collections to device storage, LoadData retrieves them. The key limitation: 1 MB on web browsers, device storage on mobile. Don’t store sensitive data - it’s not encrypted.

// Save data when online
SaveData(MyCollection, "MyLocalCache")

// Load it back when offline
LoadData(MyCollection, "MyLocalCache", true)  // true suppresses errors on first run

SaveData’s first argument has to be an actual collection, one built with Collect or ClearCollect, not an inline Table({...}) expression evaluated on the spot. That third parameter on LoadData prevents errors when no cache exists yet, essential for first-time app launches, but it’s worth pairing with an explicit check rather than relying on it alone:

If(
    IsBlankOrError(First(MyCollection).SomeField),
    ClearCollect(MyCollection, Defaults);
    SaveData(MyCollection, "MyLocalCache")
)

That guard catches the case where the load silently produced nothing and seeds a real default before anything downstream reads from an empty collection. Combine these with Connection.Connected to detect network status and switch between online and offline data sources.

Implementation Pattern

Planning Your Offline Strategy

Identify what data your app needs offline:

  • Reference Data: Equipment specs, product catalogs (read-only lookups)
  • User Input Data: Inspection forms, orders (created/updated offline)
  • App State: User preferences, sync timestamps, last screen viewed

Keep the scope lean - better performance and you won’t hit storage limits.

Caching Data at Startup

Cache essential data at app startup using App.OnStart:

// App.OnStart - Load data from appropriate source
If(
    Connection.Connected,
    // Online: get fresh data and cache it
    ClearCollect(EquipmentData, EquipmentDataSource);
    ClearCollect(WorkOrders, Filter(WorkOrdersSource, AssignedTo = User().Email));
    SaveData(EquipmentData, "EquipmentCache");
    SaveData(WorkOrders, "WorkOrderCache"),
    // Offline: load from cache
    LoadData(EquipmentData, "EquipmentCache", true);
    LoadData(WorkOrders, "WorkOrderCache", true)
);
Set(isOfflineMode, !Connection.Connected);

After this runs, EquipmentData and WorkOrders contain data from either the server or cache - your app doesn’t need to care which.

Tracking Offline Changes

Capture offline changes for later sync:

// Save button OnSelect
Collect(OfflineChanges, {
    ID: GUID(),
    RecordID: frmInspection.Item.ID,
    EntityType: "Inspection",
    ChangeType: "Update",  // or "Create", "Delete"
    ChangeData: frmInspection.Updates,
    ChangeTime: Now()
});
SaveData(OfflineChanges, "PendingChanges");

This queues changes with metadata for synchronization when connectivity returns. Two details matter here: ChangeData needs Form.Updates, which captures only the fields the user actually edited, not the whole form control (which isn’t a valid record reference on its own). And RecordID is a separate field from the change’s own ID, the record being updated, not the queued change itself, which is what the sync logic below reads.

Synchronization

Sync button pushes queued changes when online:

// Sync button OnSelect
If(Connection.Connected,
    ForAll(OfflineChanges,
        Switch(ThisRecord.ChangeType,
            "Create", Patch(InspectionsDataSource, Defaults(InspectionsDataSource), ThisRecord.ChangeData),
            "Update", Patch(InspectionsDataSource, LookUp(InspectionsDataSource, ID = ThisRecord.RecordID), ThisRecord.ChangeData),
            "Delete", Remove(InspectionsDataSource, LookUp(InspectionsDataSource, ID = ThisRecord.RecordID))
        )
    );
    Clear(OfflineChanges);
    SaveData(OfflineChanges, "PendingChanges");
    ClearCollect(EquipmentData, EquipmentDataSource);
    ClearCollect(WorkOrders, Filter(WorkOrdersSource, AssignedTo = User().Email));
    SaveData(EquipmentData, "EquipmentCache");
    SaveData(WorkOrders, "WorkOrderCache");
    // Update sync status and timestamp
    Set(lastSyncTime, Now());
    Set(syncSuccessful, true);
    ClearCollect(colSyncInfo, { lastSyncTime: lastSyncTime });
    SaveData(colSyncInfo, "SyncInfo");
    ,
    // **Offline**: if sync attempted without connection
    Set(syncSuccessful, false)
);

This code:

  1. Processes each offline change based on its type (create, update, or delete)
  2. Clears the offline changes once processed
  3. Refreshes the local caches with the latest server data
  4. Updates the last sync timestamp
  5. Sets a status flag to indicate sync success or failure

This synchronization ensures that once a connection is restored, all offline changes are applied to the backend and local caches are refreshed.

User Experience Best Practices

To create a reliable offline experience, implement these UI elements:

  1. Connection Status Indicator: Show users when they’re working offline with a subtle banner or icon
  2. Last Sync Information: Display when data was last synchronized (e.g., “Last synced: 3/6/2025 10:45 AM”)
  3. Sync Button: Give users a way to manually trigger synchronization when they’re back online
  4. Sync Status Notification: After sync, show a brief confirmation like “All changes synced!” or “Sync failed: You’re offline”

These visual cues build user confidence by making the app’s state transparent. Users need to know if they’re working with the latest data or need to sync changes.

Key Considerations

As you implement offline capabilities, keep these points in mind:

Storage Limits

  • Web Browser and Teams: SaveData is capped at 1 MB. That’s small enough that a camera control capturing even one photo can blow through it; test with the actual data types you plan to cache, not just record counts.
  • Mobile Player: No fixed cap, but bounded by available app memory rather than device storage generally. Microsoft’s own guidance is to expect roughly 30 to 70 MB of usable memory and to test on the actual target devices before assuming more.
  • Data saved in a web browser is stored unencrypted. This is a second, separate reason not to cache sensitive data locally, beyond the size limit.

Data Prioritization

  • Cache only what users truly need offline
  • For large datasets, implement filtering to cache only relevant subsets

Scope: When This Pattern Isn’t the Right One

Everything above is the SaveData/LoadData pattern, which works for any data source and gives you full control over what’s cached and how sync happens. If the app runs against Dataverse specifically, Microsoft’s native offline profiles are worth evaluating first: they handle the caching, conflict resolution, and sync queue at the platform level instead of in Power Fx you write and maintain yourself. The tradeoff is less control and a narrower set of supported scenarios. For a SharePoint-backed app, or one that needs offline behavior more customized than a Dataverse profile supports, the manual pattern in this guide is still the right tool.

Conflict Handling

In multi-user systems, you may need a strategy for handling conflicts when the same record is modified by different users while offline. Options include:

  • Last writer wins (simplest approach)
  • Timestamp comparison to determine precedence
  • Conflict flagging for manual resolution

Testing

Thoroughly test your offline implementation by:

  • Enabling airplane mode to simulate complete connection loss
  • Testing with slow or intermittent connections
  • Verifying behavior when transitioning between online and offline
  • Confirming changes sync properly when reconnected

Key Takeaways

  • Plan what data is truly needed offline. Don’t cache more than the app actually uses.
  • Cache on launch when connectivity is available, using Connection.Connected to branch between fresh data and cache.
  • Track offline changes in a local collection with enough metadata to replay them on sync.
  • Give users a clear visual indicator of online/offline status and last-sync time.
  • Test with airplane mode and intermittent connections, not just a clean online/offline toggle.

The pattern is four steps: cache when online, load from cache when offline, track changes made offline, sync when the connection returns. Our field techs went from paper forms back to the app because that pattern actually held up in the field, not because the feature existed in the abstract.

Additional Resources