Skip to content
minip2p
Esc
navigateopen⌘Jpreview
On this page

Start with React Native

Create a native endpoint, follow its component lifecycle, connect to a peer, and measure a Ping RTT.

This guide creates one minip2p endpoint for a committed React component, connects to a full peer multiaddress, and displays the usable network path and Ping RTT.

Install

npm install @minip2p/react-native
pnpm add @minip2p/react-native
yarn add @minip2p/react-native
bun add @minip2p/react-native

Because this package contains native code, rebuild the app after installing it. See Install for React Native for platform requirements and development-build commands. Expo Go is not supported.

Own the endpoint

useMinip2p constructs the endpoint after the component commits, mirrors React Native AppState, and closes the native driver during cleanup.

import {
  generateSecretKey,
  useMinip2p,
} from "@minip2p/react-native";
import { useCallback, useMemo, useState } from "react";
import { Button, Text, View } from "react-native";

export function PeerScreen({ remoteAddress }: { remoteAddress: string }) {
  const secretKey = useMemo(() => generateSecretKey(), []);
  const createConfig = useCallback(
    () => ({
      agentVersion: "my-app/0.1.0",
      secretKey,
    }),
    [secretKey]
  );
  const node = useMinip2p(createConfig);
  const endpoint = node.status === "running" ? node.endpoint : undefined;
  const [result, setResult] = useState("Not connected");

  const connect = useCallback(async () => {
    if (endpoint === undefined) return;

    try {
      const connected = await endpoint.connectAddr(remoteAddress, {
        timeoutMs: 10_000,
      });
      await endpoint.waitPeerReady(connected.peerId, {
        timeoutMs: 10_000,
      });
      const rttMs = await endpoint.ping(connected.peerId, {
        timeoutMs: 5_000,
      });
      setResult(`${connected.path.kind} · ${rttMs} ms`);
    } catch (error) {
      setResult(error instanceof Error ? error.message : String(error));
    }
  }, [endpoint, remoteAddress]);

  if (node.status === "starting") return <Text>Starting minip2p…</Text>;
  if (node.status === "failed") return <Text>Failed: {String(node.error)}</Text>;
  if (node.status === "closed") return <Text>Endpoint closed</Text>;

  return (
    <View>
      <Text selectable>{node.peerId}</Text>
      <Text selectable>{node.listenAddrs.join("\n")}</Text>
      <Button title="Connect and ping" onPress={() => void connect()} />
      <Text>{result}</Text>
    </View>
  );
}

Keep createConfig pure and stable for this component lifetime. The hook captures it once; changing the callback does not replace a running endpoint.

Understand the result

connectAddr expects a complete peer multiaddress, including the terminal /p2p/<peer-id> component. It resolves when the first usable path is ready:

path.kind Meaning
directDialed A supplied QUIC address connected directly.
directPunched Relay-assisted DCUtR produced a direct connection.
relayed Traffic is using a Circuit Relay v2 path; relayPeerId identifies the relay.

waitPeerReady is a separate milestone: it waits for Identify and returns the remote protocol list. Wait for it before opening a custom protocol stream.

endpoint.path(peerId) is the authoritative current path. A relayed result may later upgrade to directPunched.

Stop work safely

All Promise-first operations accept timeoutMs and signal. The default timeout is 65 seconds; timeoutMs: 0 disables it.

const controller = new AbortController();

try {
  const result = await endpoint.connectAddr(remoteAddress, {
    signal: controller.signal,
    timeoutMs: 10_000,
  });
  console.log(result.path);
} finally {
  controller.abort();
}

Aborting a Promise rejects it with AbortError. A timed-out Promise rejects with TimeoutError. For connect*, cancellation also cancels that native connection attempt.

Next: Endpoint lifecycle or Connections and streams.

Last updated on July 31, 2026

Was this page helpful?