Examples
Real-world usage patterns for Tide v1.2.
URL shorthand
Replace verbose fetcher boilerplate with a simple url string. Built-in handler includes credentials, abort, and AuthError detection.
const gateways = createTide({
key: 'gateways',
url: '/api/gateways',
wsPath: 'data.hermes.gateways',
}) ETag / conditional fetch
Enable etag to send If-None-Match headers. On 304, Tide skips the update — zero bandwidth, zero re-render.
const tooling = createTide({
key: 'tooling',
url: '/api/agentic/tooling',
etag: true,
pollInterval: 15000,
}) Content deduplication
hashCompare uses djb2 hashing to skip reactive updates when data is identical — prevents unnecessary DOM reconciliation.
const stack = createTide({
key: 'stack',
url: '/api/stack/status',
hashCompare: true,
pollInterval: 5000,
}) Reactive key (dynamic routes)
Pass a getter function for key and url. Tide re-fetches automatically when either changes.
const [query, setQuery] = createSignal('react')
const results = createTide({
key: () => `search-${query()}`,
url: () => `/api/search?q=${query()}`,
}) Reactive key — filter/tab switching
When using reactive keys for tab or filter UIs, each key maintains its own cache entry. Switching tabs fetches fresh data (or hits cache if within cacheTime), and the loading state resets per key.
const [period, setPeriod] = createSignal<"today" | "24h" | "7d" | "30d">("today")
const chart = createTide<ChartResponse>({
key: () => `usage-chart-${period()}`,
url: () => `/api/usage/chart?period=${period()}`,
staleTime: 30_000,
cacheTime: 300_000,
persist: true,
})
// Each period gets its own cache entry:
// - "usage-chart-today" → cached separately
// - "usage-chart-24h" → cached separately
// UI: skeleton while loading, instant on cache hit
return (
<div>
<div class="flex gap-1">
<For each={["today", "24h", "7d", "30d"] as const}>
{(p) => (
<button
class={period() === p ? "active" : ""}
onClick={() => setPeriod(p)}
>
{p}
</button>
)}
</For>
</div>
<Show when={!chart.loading() && chart.data()} fallback={<ChartSkeleton />}>
<Chart data={chart.data()!} />
</Show>
</div>
) Conditional fetch (enabled)
Use enabled to pause all activity (fetch, poll, WS) until a condition is met.
const [loggedIn, setLoggedIn] = createSignal(false)
const data = createTide({
key: 'private',
url: '/api/private',
enabled: () => loggedIn(),
}) wsPath shorthand
Use wsPath instead of a ws function for simple dot-notation extraction from WebSocket messages.
// Before (v1.0)
ws: (msg) => msg?.data?.stack ?? null
// After (current — standard)
wsPath: 'data.stack' WS backoff + heartbeat
<TideProvider
ws={{ url: 'wss://server/ws' }}
reconnect={{ baseMs: 1000, maxMs: 30000 }}
heartbeat={25000}
>
<App />
</TideProvider> pause() / resume() — lifecycle control
NEW in v1.2.0. Use pause() before optimistic mutations to stop polling and abort inflight requests. Call resume() + await refresh() after the operation to re-sync with the server.
async function handleStart() {
tide.pause() // stop polling, prevent overwrite
tide.mutate(prev => ({ ...prev, status: 'starting' }))
await fetch('/api/start', { method: 'POST' })
tide.resume() // restart polling
await tide.refresh() // wait for fresh data
// UI now reflects real server state
}