Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 86 additions & 99 deletions docs/concepts/caching.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: "Caching & Offline Support"
description: "Learn how Stac intelligently caches screens for offline access, faster loading, and smarter network usage"
---

Stac includes a powerful caching system that stores screens locally, enabling offline access, instant loading, and more efficient network usage. The caching layer works automatically with the `Stac` widget and can be customized for different use cases.
Stac includes a powerful caching system that stores screens locally, enabling offline access, instant loading, and more efficient network usage. The caching layer works automatically with the `Stac` widget and is configured globally during initialization.

## How Caching Works

Expand All @@ -14,42 +14,72 @@ When you use the `Stac` widget to fetch screens from Stac Cloud:
3. **Version Tracking**: Each cached screen stores its version number, enabling smart updates
4. **Background Updates**: Fresh data can be fetched in the background without blocking the UI

## Global Configuration

Configure caching once during app initialization. This applies to all `Stac` widgets and `StacAppTheme`:

```dart
await Stac.initialize(
options: defaultStacOptions,
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.networkFirst, // default
maxAge: Duration(days: 30),
),
);
```

## Cache Strategies

Stac provides five caching strategies to fit different use cases:

### 1. Optimistic (Default)
### 1. Network First (Default)

Returns cached data immediately while fetching updates in the background. Best for fast perceived performance.
Always tries the network first, falls back to cache if network fails. **This is the default strategy.**

```dart
Stac(
routeName: '/home',
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.optimistic,
),
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.networkFirst,
)
```

**Behavior:**
- ✅ Always fetches fresh data first
- ✅ Falls back to cache on network error
- ✅ Ensures latest content when online
- 🌐 Requires network for best experience

**Best for:** Server-driven UI where users should see the latest content, real-time data, frequently changing screens.

### 2. Optimistic

Returns cached data immediately while fetching updates in the background.

```dart
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.optimistic,
)
```

**Behavior:**
- ✅ Returns cached data instantly (even if expired)
- ✅ Returns cached data instantly
- ✅ Fetches fresh data in background
- ✅ Updates cache for next load
- ⚡ Fastest perceived loading

**Best for:** UI layouts, content screens, any screen where instant loading matters more than showing the absolute latest data.

### 2. Cache First
<Warning>
With optimistic caching, users see outdated content until the next app launch after you deploy updates. Consider using `networkFirst` if immediate updates are important.
</Warning>
Comment on lines +35 to +73
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify optimistic warning timing to avoid over-promising “next app launch.”
The warning implies updates only appear after an app relaunch, but the cache should update on the next load after the background refresh completes. Consider tightening the wording.

📝 Suggested wording tweak
-  With optimistic caching, users see outdated content until the next app launch after you deploy updates. Consider using `networkFirst` if immediate updates are important.
+  With optimistic caching, users may see outdated content until the next time the screen is loaded after a background refresh completes. Consider using `networkFirst` if immediate updates are important.
🤖 Prompt for AI Agents
In `@docs/concepts/caching.mdx` around lines 35 - 73, Update the Optimistic
caching warning copy to avoid implying updates only appear after an app
relaunch; instead state that cached data is returned immediately and the
background refresh will update the cache so fresh content will be visible on the
next load once the refresh completes. Edit the Warning block under the
StacCacheConfig / StacCacheStrategy.optimistic example to replace the current
“until the next app launch” phrasing with language indicating the cache is
refreshed in the background and the updated content will be shown on the next
view/load after the refresh finishes.


### 3. Cache First

Uses cached data if available and valid, only fetches from network when cache is invalid or missing.

```dart
Stac(
routeName: '/home',
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheFirst,
maxAge: Duration(hours: 24),
),
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheFirst,
maxAge: Duration(hours: 24),
)
```

Expand All @@ -61,37 +91,13 @@ Stac(

**Best for:** Offline-first apps, content that doesn't change frequently, reducing network usage.

### 3. Network First

Always tries the network first, falls back to cache if network fails.

```dart
Stac(
routeName: '/home',
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.networkFirst,
),
)
```

**Behavior:**
- ✅ Always fetches fresh data first
- ✅ Falls back to cache on network error
- ✅ Ensures latest content when online
- 🌐 Requires network for best experience

**Best for:** Real-time data, frequently changing content, screens where freshness is critical.

### 4. Cache Only

Only uses cached data, never makes network requests. Throws an error if no cache exists.

```dart
Stac(
routeName: '/home',
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheOnly,
),
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheOnly,
)
```

Expand All @@ -108,11 +114,8 @@ Stac(
Always fetches from network, never uses or updates cache.

```dart
Stac(
routeName: '/home',
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.networkOnly,
),
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.networkOnly,
)
```

Expand All @@ -124,47 +127,37 @@ Stac(

**Best for:** Sensitive data that shouldn't be cached, real-time dashboards, authentication screens.

## Cache Configuration
## Configuration Options

### StacCacheConfig Properties

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `strategy` | `StacCacheStrategy` | `optimistic` | The caching strategy to use |
| `maxAge` | `Duration?` | `null` | Maximum age before cache is considered expired. `null` means no time-based expiration |
| `refreshInBackground` | `bool` | `true` | Whether to fetch fresh data in background when cache is valid |
| `staleWhileRevalidate` | `bool` | `false` | Whether to use expired cache while fetching fresh data |
| `strategy` | `StacCacheStrategy` | `networkFirst` | The caching strategy to use |
| `maxAge` | `Duration?` | `null` | Maximum age before cache is considered stale. `null` means no time-based expiration |
| `refreshInBackground` | `bool` | `true` | Whether to fetch fresh data in background when showing cached content |

### Setting Cache Duration

Control how long cached data remains valid:

```dart
// Cache valid for 1 hour
Stac(
routeName: '/home',
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheFirst,
maxAge: Duration(hours: 1),
),
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheFirst,
maxAge: Duration(hours: 1),
)

// Cache valid for 7 days
Stac(
routeName: '/settings',
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheFirst,
maxAge: Duration(days: 7),
),
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheFirst,
maxAge: Duration(days: 7),
)

// Cache never expires (only version-based updates)
Stac(
routeName: '/static-page',
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheFirst,
maxAge: null, // No time-based expiration
),
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheFirst,
maxAge: null, // No time-based expiration
)
```

Expand All @@ -173,13 +166,10 @@ Stac(
Keep cache fresh without blocking the UI:

```dart
Stac(
routeName: '/home',
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheFirst,
maxAge: Duration(hours: 1),
refreshInBackground: true, // Fetch updates silently
),
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheFirst,
maxAge: Duration(hours: 1),
refreshInBackground: true, // Fetch updates silently
)
```

Expand All @@ -189,33 +179,13 @@ When `refreshInBackground` is `true`:
- Cache is updated for next load
- User sees instant loading with eventual consistency

### Stale While Revalidate

Show expired cache while fetching fresh data:

```dart
Stac(
routeName: '/home',
cacheConfig: StacCacheConfig(
strategy: StacCacheStrategy.cacheFirst,
maxAge: Duration(hours: 1),
staleWhileRevalidate: true, // Use expired cache while fetching
),
)
```

This is useful when:
- You prefer showing something over a loading spinner
- Content staleness is acceptable for a brief period
- Network is slow or unreliable

## Strategy Comparison

| Strategy | Initial Load | Subsequent Load | Offline Support | Best For |
|----------|--------------|-----------------|-----------------|----------|
| `optimistic` | Network → Cache | Cache (bg update) | ✅ Yes | Fast UX |
| `networkFirst` | Network | Network (cache fallback) | ⚠️ Fallback only | Fresh data (default) |
| `optimistic` | Cache or Network | Cache (bg update) | ✅ Yes | Fast UX |
| `cacheFirst` | Cache or Network | Cache | ✅ Yes | Offline apps |
| `networkFirst` | Network | Network (cache fallback) | ⚠️ Fallback only | Fresh data |
| `cacheOnly` | Cache only | Cache only | ✅ Yes | Offline mode |
| `networkOnly` | Network only | Network only | ❌ No | Sensitive data |

Expand All @@ -230,3 +200,20 @@ Stac tracks version numbers for each cached screen. When you deploy updates to S

This ensures users get updates without manual intervention while maintaining fast loading times.

## Clearing Cache

Clear cached screens programmatically:

```dart
// Clear a specific screen
await StacCloud.clearScreenCache('/home');

// Clear all cached screens
await StacCloud.clearAllCache();

// Clear a specific theme
await StacCloud.clearThemeCache('dark');

// Clear all cached themes
await StacCloud.clearAllThemeCache();
```
4 changes: 4 additions & 0 deletions packages/stac/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.3.0-dev.1

- **BREAKING**: Refactored caching to use global configuration via `Stac.initialize()`. See [Caching Docs](https://docs.stac.dev/concepts/caching) for migration guide.

## 1.2.0

- Added screen caching and offline support
Expand Down
1 change: 1 addition & 0 deletions packages/stac/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ This approach separates your app's presentation layer from its business logic, e
- 🎛 Actions & navigation: Control routes and API calls from the backend.
- 📝 Forms & validation: Built-in form state and validation rules.
- 🎨 Theming: Brand and layout via JSON with Stac Theme.
- 💾 Caching: Intelligent screen caching with configurable strategies.
- 🔌 Extensible: Add custom widgets, actions, and native integrations.

## Documentation
Expand Down
Loading