---
title: App Network Plugin
related:
  - title: App Visibility Plugin
    path: app-visibility.md
---
The AppNetwork plugin keeps a reactive picture of the network the browser is on. It wraps the [Navigator.onLine](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine) property together with the `online`/`offline` events and, where the browser exposes it, the [Network Information API](https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API).

## AppNetwork API

### Props

- `online` (boolean, optional, reactive)
  Is the browser connected to a network? 'false' is reliable (the browser knows it is offline); 'true' only means that a network is present, not that the Internet is reachable
- `hasConnectionInfo` (boolean, optional, reactive)
  Does the browser expose the Network Information API (navigator.connection)? When 'false', all the connection props stay 'undefined'
- `effectiveType` (string, optional, reactive)
  Effective connection type, as estimated by the browser from the measured round-trip time and downlink speed; 'undefined' when the browser does not expose it
  Accepts: `'slow-2g'`, `'2g'`, `'3g'`, `'4g'`
- `downlink` (number, optional, reactive)
  Effective bandwidth estimate (in megabits per second), rounded by the browser; 'undefined' when the browser does not expose it
  Examples: `10`, `1.45`, `void 0`
- `rtt` (number, optional, reactive)
  Effective round-trip time estimate (in milliseconds), rounded by the browser; 'undefined' when the browser does not expose it
  Examples: `50`, `300`, `void 0`
- `saveData` (boolean, optional, reactive)
  Has the user asked for reduced data usage (the browser's Data Saver / Lite mode)? 'undefined' when the browser does not expose it

### Vue Injection

Accessible via `$q.network` (e.g., `this.$q.network` in Options API or `useQuasar().network` in Composition API).

## Installation

Add to `quasar.config.js`:

```js
framework: {
    plugins: [
      'AppNetwork'
    ]
}
```

## Usage

```js
// outside of a Vue file
import { AppNetwork } from 'quasar'
AppNetwork.online // Boolean

// inside of a Vue file
import { useQuasar } from 'quasar'
setup () {
  const $q = useQuasar()
  // now use $q.network.online (Boolean)
}
```

Example "AppNetwork":

```vue
<template>
  <div>
    Toggle your network connection (or throttle it from the browser's
    DevTools) to see the values change.
  </div>

  <q-markup-table class="q-mt-md" flat bordered>
    <tbody>
      <tr>
        <td>online</td>
        <td>{{ $q.network.online }}</td>
      </tr>
      <tr>
        <td>hasConnectionInfo</td>
        <td>{{ $q.network.hasConnectionInfo }}</td>
      </tr>
      <tr>
        <td>effectiveType</td>
        <td>{{
          $q.network.effectiveType ?? 'not exposed by this browser'
        }}</td>
      </tr>
      <tr>
        <td>downlink</td>
        <td>{{ $q.network.downlink ?? 'not exposed by this browser' }}</td>
      </tr>
      <tr>
        <td>rtt</td>
        <td>{{ $q.network.rtt ?? 'not exposed by this browser' }}</td>
      </tr>
      <tr>
        <td>saveData</td>
        <td>{{ $q.network.saveData ?? 'not exposed by this browser' }}</td>
      </tr>
    </tbody>
  </q-markup-table>

  <q-markup-table v-if="eventList.length > 0" class="q-mt-md" flat bordered>
    <tbody>
      <tr v-for="evt in eventList" :key="evt.timestamp">
        <td>{{ evt.timestamp }}</td>
        <td>{{ evt.label }}</td>
      </tr>
    </tbody>
  </q-markup-table>
</template>

<script setup>
import { useQuasar } from 'quasar'
import { ref, watch } from 'vue'

function pad(number) {
  return (number < 10 ? '0' : '') + number
}

const $q = useQuasar()
const eventList = ref([])

watch(
  () => $q.network.online,
  state => {
    const date = new Date()
    eventList.value.unshift({
      timestamp:
        pad(date.getHours()) +
        ':' +
        pad(date.getMinutes()) +
        ':' +
        pad(date.getSeconds()) +
        '.' +
        date.getMilliseconds(),
      label: state ? 'Back online' : 'Went offline'
    })
  }
)
</script>
```

> [!WARNING]
> **What "online" really means**
>
> `online` being `false` is reliable: the browser knows that it has no network. `online` being `true` only tells you that some network is present. A captive portal, a router with no upstream or a dead Wi-Fi link all still report `true`, so treat it as a hint and let your actual requests have the final word.

## Connection details

Chromium-based browsers (Chrome, Edge, Opera, Samsung Internet, Android WebView) also expose the Network Information API, which the plugin mirrors into `effectiveType`, `downlink`, `rtt` and `saveData`. Firefox and Safari do not implement it, so on those browsers the four props stay `undefined` and `hasConnectionInfo` is `false`.

```html
<template>
  <q-banner v-if="$q.network.saveData" class="bg-grey-3">
    Data Saver is on, so images load at a lower quality.
  </q-banner>
</template>
```

The values are the browser's own estimates, rounded on purpose (they are a fingerprinting vector), and they describe the link, not the far end: a `4g` effective type says nothing about how quickly your API answers.

> [!TIP]
> **Hybrid apps**
>
> Capacitor and Cordova apps can additionally rely on their native network plugins, which know about the device's radio state. AppNetwork stays the web-side answer, so it also works inside a WebView, on Electron and as a PWA.

## Watching for status change

```html
<template>...</template>

<script setup>
  import { useQuasar } from 'quasar'
  import { watch } from 'vue'

  const $q = useQuasar()

  watch(
    () => $q.network.online,
    val => {
      console.log(val ? 'Back online' : 'Went offline')
    }
  )
</script>
```

## SSR and SSG

On the server there is no network to observe, so `$q.network.online` is `true`, `hasConnectionInfo` is `false` and the connection props are `undefined`. On the client, the plugin only fills in the real values after hydration completes, which keeps the server-rendered markup from mismatching when the page was loaded offline (for example from a PWA cache).
