Skip to content

About

"React SWR" ported for Jetpack Compose & Compose Multiplatform

Topics

Resources

Stars

66 stars

Watchers

0 watching

Forks

Repository files navigation

SWR for Compose

Maven Central javadoc Check License

This library is inspired by React SWR ported for Jetpack Compose and Compose Multiplatform.
Supported platforms are Android, Desktop, iOS and Web.

The API specification of React SWR is followed as much as possible.
Options are also supported for the most part.

What's "SWR"?

According to React SWR, "SWR" refers to

The name “SWR” is derived from stale-while-revalidate, a HTTP cache invalidation strategy popularized by HTTP RFC 5861. SWR is a strategy to first return the data from cache (stale), then send the fetch request (revalidate), and finally come with the up-to-date data.

Requirement

Install

Add the following gradle dependency exchanging *.*.* for the latest release. Maven Central

implementation("com.kazakago.swr:swr-compose:*.*.*")

Getting Started

As with React SWR, implement the "fetcher function" and set it with the key to rememberSWR().
Using Kotlin's Destructuring declarations for return values can be written in the same way as in React SWR.

private val fetcher: suspend (key: String) -> String = {
    getNameApi.execute(key)
}

@Composable
fun Profile() {
    val (data, error) = rememberSWR("/api/user", fetcher)

    if (error != null) {
        Text("failed to load")
    } else if (data == null) {
        Text("loading...")
    } else {
        Text("hello $data!")
    }
}

Example

Live demo of Kotlin/Wasm is here.

Refer to the example module for details. This module works as a Compose Multiplatform app.

Supported Features

Feature name Note
Global Configuration
Data Fetching
Error Handling
Auto Revalidation
Conditional Data Fetching
Arguments
Mutation Available by rememberSWRMutation()
Pagination Available by rememberSWRInfinite()
Prefetching Data Available by rememberSWRPreload()
Subscription Available by rememberSWRSubscription()

Supported Options

The following options are supported for React SWR.
https://swr.vercel.app/docs/options

Option name Default value Description
revalidateIfStale true Revalidate when cached data is stale
revalidateOnMount true Revalidate when component mounts
revalidateOnFocus true Revalidate when app regains focus
revalidateOnReconnect true Revalidate when network reconnects
refreshInterval 0.seconds Polling interval for periodic refresh (0=disabled)
refreshWhenHidden false Continue polling when app is in background
refreshWhenOffline false Continue polling when offline
shouldRetryOnError true Retry fetching on error
dedupingInterval 2.seconds Time window to deduplicate identical requests
focusThrottleInterval 5.seconds Min interval between focus-triggered revalidations
loadingTimeout 3.seconds Timeout before onLoadingSlow callback fires
errorRetryInterval 5.seconds Wait time between error retries
errorRetryCount Maximum number of error retries
fallbackData Data to display while loading
onLoadingSlow Callback when loading exceeds loadingTimeout
onSuccess Callback on successful data fetch
onError Callback on fetch error
keepPreviousData false Keep previous data when key changes
isPaused Pause all revalidation when returns true
onErrorRetry Exponential backoff Custom error retry handler

Mutation

rememberSWRMutation() provides a way to mutate remote data. Unlike rememberSWR(), it does not automatically fetch data — mutations are triggered manually.

private val fetcher: suspend (key: String, arg: String) -> String = { key, arg ->
    updateNameApi.execute(key, arg)
}

@Composable
fun UpdateProfile() {
    val (trigger, isMutating) = rememberSWRMutation("/api/user", fetcher)

    Button(
        onClick = { scope.launch { trigger("new name") } },
        enabled = !isMutating,
    ) {
        Text(if (isMutating) "Updating..." else "Update")
    }
}

Mutation Options

Option name Default value Description
optimisticData Data to display immediately before mutation resolves
revalidate false Revalidate cached data after mutation
populateCache false Update cache with mutation result
rollbackOnError true Revert to previous data if mutation fails
onSuccess Callback on successful mutation
onError Callback on mutation error

Subscription

rememberSWRSubscription() integrates with real-time data sources such as WebSockets, Server-Sent Events, or Firestore. Data received via the subscription is written to the SWR cache, so any rememberSWR() using the same key automatically displays the latest data.

@Composable
fun LiveFeed() {
    val (data, error) = rememberSWRSubscription(
        key = "/ws/feed",
        subscribe = { key ->
            callbackFlow {
                val ws = WebSocket(key) { message ->
                    trySend(message)
                }
                awaitClose { ws.close() }
            }
        },
    )

    if (error != null) {
        Text("error: ${error.message}")
    } else {
        Text("latest: ${data ?: "..."}")
    }
}

Pagination / Infinite Loading

rememberSWRInfinite() provides paginated data fetching with dynamic page management.

@Composable
fun UserList() {
    val (data, error, isValidating, isLoading, _, size, setSize) = rememberSWRInfinite(
        getKey = { pageIndex, previousPageData ->
            if (previousPageData != null && previousPageData.isEmpty()) null // reached the end
            else "/api/users?page=$pageIndex"
        },
        fetcher = { key -> fetchUsers(key) },
    )

    // data is List<List<User>?> — one entry per page
    data?.flatten()?.forEach { user ->
        Text(user.name)
    }

    Button(onClick = { setSize(size + 1) }) {
        Text("Load More")
    }
}

Pagination-specific options can be set in the config block:

Option name Default value Description
initialSize 1 Number of pages to load initially
revalidateAll false Revalidate all loaded pages
revalidateFirstPage true Revalidate the first page on changes
persistSize false Persist the page count across re-mounts

Immutable Data

rememberSWRImmutable() fetches data once and never revalidates automatically. Useful for data that doesn't change (e.g., static resources, user settings loaded once).

@Composable
fun StaticContent() {
    val (data, error) = rememberSWRImmutable("/api/config", fetcher)
    // ...
}

This is equivalent to rememberSWR() with all revalidation options disabled.

Persistent Cache

You can provide a Persister to save fetched data to local storage (e.g., database, file system). This allows data to survive app restarts.

val persister = Persister<String, User>(
    loadData = { key -> database.getUser(key) },
    saveData = { key, data -> database.saveUser(key, data) },
)

@Composable
fun Profile() {
    val (data, error) = rememberSWR("/api/user", fetcher, persister)
    // On first load, persister.loadData is called for immediate display,
    // then fetcher runs in background to revalidate.
}

Notice for performance

Deduplication and Deep Comparison are supported, but Dependency Collection is not supported due to Kotlin's language specification.
Therefore, the number of re-rendering (re-compose) may be higher than in the original React SWR.

However, it is possible to prevent performance degradation by limiting the number of arguments passed to child Composable functions (e.g., only data).

About

"React SWR" ported for Jetpack Compose & Compose Multiplatform

Topics

Resources

Stars

66 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages