> ## Documentation Index
> Fetch the complete documentation index at: https://developers.learn.ink/llms.txt
> Use this file to discover all available pages before exploring further.

# Offline behaviour

> How the SDK shows downloaded content without a connection, and the one screen your app must provide

<Note>
  **Beta.** Offline support depends on both the SDK and the LearnInk web app's offline
  capability, which is rolling out alongside this beta. If offline content does not appear
  for your organisation yet, contact [support@learn.ink](mailto:support@learn.ink) to
  confirm it is enabled.
</Note>

LearnInk caches content on the device as users browse it. The SDK makes that cache usable
when the device has no connection — important for field workers, rural users, and anyone
on an unreliable network.

## What happens with no connection

<Steps>
  <Step title="The SDK notices there is no usable connection">
    It checks before doing anything else, so an offline launch is immediate rather than
    waiting for a network request that cannot succeed.
  </Step>

  <Step title="It skips the token request entirely">
    There is no point asking your backend for a sign-in token with no network. The user's
    existing signed-in session on the device is used instead.
  </Step>

  <Step title="LearnInk loads from the device cache">
    Previously downloaded content appears, and LearnInk shows its own offline indicator.
    Your app fires `onOfflineModeEntered` — usually you need do nothing here.
  </Step>

  <Step title="If nothing was downloaded, your app must step in">
    With no cached content, LearnInk cannot load at all, so it cannot show its own
    message. Your app fires `onOfflineContentUnavailable` and **you** show a screen.
  </Step>
</Steps>

## The one thing you must implement

`onOfflineContentUnavailable` is the only offline callback that requires action. Without
it, the user sees a blank screen.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  learnInk.eventListener = object : LearnInkEventListener {
      override fun onOfflineContentUnavailable() {
          // Show your own message in place of the LearnInk view
          noOfflineContentView.visibility = View.VISIBLE
      }

      override fun onConnectivityRestored() {
          // Nothing to interrupt on an error screen — just retry
          if (noOfflineContentView.isVisible) {
              noOfflineContentView.visibility = View.GONE
              learnInk.refreshSession()
          }
      }
  }
  ```

  ```dart Flutter theme={null}
  LearnInkWebView(
    // …
    onOfflineContentUnavailable: () =>
        setState(() => _contentUnavailable = true),
    onConnectivityRestored: () {
      if (_contentUnavailable) {
        setState(() => _contentUnavailable = false);
        _controller?.refreshSession();
      }
    },
  )
  ```

  ```tsx React Native theme={null}
  <LearnInkWebView
    // …
    onOfflineContentUnavailable={() => setContentUnavailable(true)}
    onConnectivityRestored={() => {
      if (contentUnavailable) {
        setContentUnavailable(false);
        learnInkRef.current?.refreshSession();
      }
    }}
  />
  ```
</CodeGroup>

Wording that works well:

> **No offline content downloaded**
> Connect to the internet and open LearnInk once, so your content is saved for offline use.
> **\[ Retry ]**

<Note>
  Keep the LearnInk view mounted underneath your message rather than removing it — that way
  your Retry button can reload it. Overlay your screen on top instead of replacing it.
</Note>

## What you should not implement

<Warning>
  **Do not add your own "You are offline" banner.** LearnInk already shows one inside the
  WebView whenever it is serving cached content. A second banner in your app's chrome
  duplicates it and confuses users. `onOfflineModeEntered` exists for analytics or custom
  chrome, not for a banner.
</Warning>

## Poor connections, not just no connection

A network that is technically connected but not passing traffic — a weak signal, a captive
portal, a hotel Wi-Fi splash page — is harder to detect than being fully offline, because
the device still reports itself as online.

The SDK handles this by showing downloaded content once the token request misses a short
deadline (`offlineFirstDelayMillis`, one second by default) rather than making the user
wait for the full timeout. The token request continues in the background, and if it
eventually succeeds the page reloads signed-in.

The practical effect: on a bad connection users see their content in about a second
instead of staring at a spinner for five.

## Requirements and limits

* **The user must have opened LearnInk online at least once on that device.** There is no
  cache before the first successful visit, and no way to pre-download content on a device
  that has never connected.
* **Clearing your app's data clears the cache**, so the next offline launch shows the
  "no offline content" screen.
* **Progress made offline syncs when connectivity returns** — users do not lose work.
* **The SDK does not reload automatically** when the network comes back, to avoid
  interrupting someone mid-lesson. LearnInk recovers by itself; use
  `onConnectivityRestored` only to rescue the "nothing downloaded" screen.

## Turning it off

If you would rather always require a live connection:

<CodeGroup>
  ```kotlin Kotlin theme={null}
  LearnInkConfig(orgId = "your-org-id", offlineSupportEnabled = false)
  ```

  ```dart Flutter theme={null}
  LearnInkWebView(orgId: 'your-org-id', offlineSupportEnabled: false, /* … */)
  ```

  ```tsx React Native theme={null}
  <LearnInkWebView orgId="your-org-id" offlineSupportEnabled={false} /* … */ />
  ```
</CodeGroup>

## Testing it

<Steps>
  <Step title="Open LearnInk with a connection and browse a little">
    This populates the cache.
  </Step>

  <Step title="Fully close your app">
    Swipe it away — do not just background it.
  </Step>

  <Step title="Turn on airplane mode">
    This is a cleaner test than a weak signal, because the device definitely reports
    itself offline.
  </Step>

  <Step title="Reopen your app and go to LearnInk">
    You should see your downloaded content, with LearnInk's own offline indicator. No
    login screen, and no five-second wait.
  </Step>

  <Step title="Then test the empty case">
    Clear your app's data, stay in airplane mode, and open LearnInk. You should see the
    "no offline content" screen you built above.
  </Step>
</Steps>
