> ## Documentation Index
> Fetch the complete documentation index at: https://www.offlineprotocol.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Setup - React Native

## Prerequisites

* Node.js `18.x` or higher
* React `18.x` or `19.x`
* React Native or Expo
* A package manager: `npm` or `yarn`

## 1. Install the SDK

Install the SDK and its peer dependencies:

```bash theme={null}
npm install @offline-protocol/id-react-native
```

Or with Yarn:

```bash theme={null}
yarn add @offline-protocol/id-react-native
```

## 2. Import the SDK

```ts theme={null}
import { OfflineAppProvider } from "@offline-protocol/id-react-native";
```

## 3. Wrap your app with `OfflineAppProvider`

Pass your **Project ID** from the Offline Dashboard. The provider initializes the SDK, restores any saved session on launch, and makes auth state available to hooks throughout your app.

### Expo Router

```tsx theme={null}
import { Stack } from "expo-router";
import { OfflineAppProvider } from "@offline-protocol/id-react-native";

const PROJECT_ID = "your-project-id-here";

export default function RootLayout() {
  return (
    <OfflineAppProvider projectId={PROJECT_ID}>
      <Stack />
    </OfflineAppProvider>
  );
}
```

### React Native (App entry)

```tsx theme={null}
import { OfflineAppProvider } from "@offline-protocol/id-react-native";
import App from "./App";

const PROJECT_ID = "your-project-id-here";

export default function Root() {
  return (
    <OfflineAppProvider projectId={PROJECT_ID}>
      <App />
    </OfflineAppProvider>
  );
}
```

### Optional: custom location provider

If you use proof-of-location features, pass a function that returns the device's coordinates:

```tsx theme={null}
import * as Location from "expo-location";
import {
  OfflineAppProvider,
  type LocationProvider,
} from "@offline-protocol/id-react-native";

const locationProvider: LocationProvider = async () => {
  const { coords } = await Location.getCurrentPositionAsync({});
  return { lat: coords.latitude, lon: coords.longitude };
};

export default function RootLayout() {
  return (
    <OfflineAppProvider
      projectId={PROJECT_ID}
      locationProvider={locationProvider}
    >
      {/* app content */}
    </OfflineAppProvider>
  );
}
```

## 4. Verify your setup

Use the `useAuth` hook to read auth state. On launch, the SDK asynchronously restores a session from the stored token — check `loading` before treating `user === null` as logged out.

```tsx theme={null}
import { Text } from "react-native";
import { useAuth } from "@offline-protocol/id-react-native";

function TestComponent() {
  const { user, loading, sessionRestoreError } = useAuth();

  if (loading) {
    return <Text>Loading session…</Text>;
  }

  if (sessionRestoreError?.reason === "network") {
    return <Text>Offline — session will restore when connected</Text>;
  }

  return (
    <Text>
      SDK connected! User: {user?.email ?? "Not authenticated"}
    </Text>
  );
}
```

### Session restore errors

When a stored token cannot be validated, `sessionRestoreError` is set with a typed `reason`:

| Reason           | Meaning                                 | SDK behavior                     |
| ---------------- | --------------------------------------- | -------------------------------- |
| `"network"`      | DNS failure, timeout, or device offline | Token is kept; retry when online |
| `"unauthorized"` | Token expired or invalid (401/403)      | Token is cleared                 |
| `"unknown"`      | Server error or unexpected response     | Token is kept                    |

## 5. Implement user authentication

Call `loginWithModal()` to open the built-in login bottom sheet. It returns `void` — the modal handles email OTP verification and username registration internally.

```tsx theme={null}
import { TouchableOpacity, Text } from "react-native";
import { useAuth } from "@offline-protocol/id-react-native";

function LoginButton() {
  const { loginWithModal, user, logout } = useAuth();

  if (user) {
    return (
      <>
        <Text>Welcome, {user.email}!</Text>
        <TouchableOpacity onPress={logout}>
          <Text>Log out</Text>
        </TouchableOpacity>
      </>
    );
  }

  return (
    <TouchableOpacity onPress={loginWithModal}>
      <Text>Login with Offline ID</Text>
    </TouchableOpacity>
  );
}

export default LoginButton;
```

## API reference

### `OfflineAppProvider`

| Prop               | Type               | Required | Description                                  |
| ------------------ | ------------------ | -------- | -------------------------------------------- |
| `projectId`        | `string`           | Yes      | Project ID from the Offline Dashboard        |
| `locationProvider` | `LocationProvider` | No       | Returns `{ lat, lon }` for proof-of-location |
| `children`         | `ReactNode`        | Yes      | App content                                  |

### `useAuth()`

| Field                 | Type                                                           | Description                                  |
| --------------------- | -------------------------------------------------------------- | -------------------------------------------- |
| `user`                | `UserAccount \| null`                                          | Current authenticated user                   |
| `token`               | `string \| null`                                               | Bearer token for API requests                |
| `loading`             | `boolean`                                                      | `true` while session restore is in progress  |
| `sessionRestoreError` | `SessionRestoreFailedError \| null`                            | Set when session restore fails               |
| `loginWithModal`      | `() => void`                                                   | Opens the built-in login bottom sheet        |
| `logout`              | `() => void`                                                   | Clears user, token, and stored auth token    |
| `sendCode`            | `(email: string) => Promise<boolean>`                          | Sends OTP to email                           |
| `verifyCode`          | `(email: string, otp: string) => Promise<UserAccount \| null>` | Verifies OTP and signs in                    |
| `refreshUser`         | `() => Promise<void>`                                          | Re-fetches the current user from `/users/me` |
| `registerUsername`    | `(username: string) => Promise<Profile \| null>`               | Registers a username for the current user    |
| `isUsernameAvailable` | `(username: string) => Promise<boolean>`                       | Checks username availability                 |

### Other hooks

```ts theme={null}
import {
  useProfiles,
  useConnections,
  useProofOfLocation,
} from "@offline-protocol/id-react-native";
```

| Hook                   | Purpose                                 |
| ---------------------- | --------------------------------------- |
| `useProfiles()`        | List, search, and count user profiles   |
| `useConnections()`     | Manage peer connections                 |
| `useProofOfLocation()` | Register location via proof-of-location |
