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

# Quick Start

> Get up and running with PNTA Expo client in minutes

## Prerequisites

<Note>
  Make sure you've completed the [Platform
  Setup](/guides/react-native/platform-setup) for iOS and Android before
  proceeding.
</Note>

## How to Use

Create a client, request permission, identify the device's push token, and handle notification taps:

```tsx app/_layout.tsx theme={null}
import { useEffect } from "react";
import { Platform } from "react-native";
import { router, Stack } from "expo-router";
import * as Notifications from "expo-notifications";
import { createPntaClient } from "@pnta/expo";

// Handle notifications that arrive while the app is in the foreground.
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowBanner: true,
    shouldShowList: true,
    shouldPlaySound: true,
    shouldSetBadge: true,
  }),
});

const pnta = createPntaClient({ projectId: "prj_XXXXXXXXX" });

export default function RootLayout() {
  useEffect(() => {
    // Identify current push token with PNTA.
    registerForPushNotificationsAsync().then((token) => token && pnta.identify(token));

    // Push tokens can rotate; re-identify when that happens.
    const subscription = Notifications.addPushTokenListener((token) =>
      pnta.identify(token.data),
    );

    // Track notification opens on tap.
    const tapSubscription = pnta.onNotificationTap((payload) => {
      // Deep link to the notification's link_to, if it has one.
      if (payload.link_to) router.push(payload.link_to);
    });

    return () => {
      subscription.remove();
      tapSubscription.remove();
    };
  }, []);

  return <Stack />;
}

// Prompts for notification permission.
async function registerForPushNotificationsAsync() {
  if (Platform.OS === "android") {
    await Notifications.setNotificationChannelAsync("default", {
      name: "default",
      importance: Notifications.AndroidImportance.MAX,
      vibrationPattern: [0, 250, 250, 250],
      lightColor: "#FF231F7C",
    });
  }

  const { status: existingStatus } = await Notifications.getPermissionsAsync();
  let finalStatus = existingStatus;
  if (existingStatus !== "granted") {
    const { status } = await Notifications.requestPermissionsAsync();
    finalStatus = status;
  }
  if (finalStatus !== "granted") return;

  try {
    return (await Notifications.getDevicePushTokenAsync()).data;
  } catch (e) {
    console.warn("Failed to get push token", e);
  }
}
```

<Tip>
  Get your project ID from your [PNTA Dashboard](https://app.pnta.io)
  settings.
</Tip>

**That's it!** Your app now receives push notifications.

`getDevicePushTokenAsync()` returns the native APNs or FCM token. PNTA uses it as the device identifier.

***

## Advanced Usage

### Metadata

Set custom data on the device after login. Use it to target users from the [API](/api-reference/introduction) or in topic rules:

```tsx theme={null}
await pnta.setMetadata(token, {
  user_id: "123",
  user_email: "user@example.com",
});
```

### Delayed Registration

Ask for permission later, for example after signup, when users understand the value:

```tsx theme={null}
async function onSignup() {
  const token = await registerForPushNotificationsAsync();
  if (token) await pnta.identify(token);
}
```

<Tip>
  **Best Practice:** Consider delayed registration for better opt-in rates.
</Tip>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.