TOOLKIT·ARCADE

Best Practices

Patterns and tips for building robust, scalable P2P applications with Toolkit-P2P.

🔄 State Management

Use CRDTs for Shared State

Conflict-free Replicated Data Types (CRDTs) automatically handle concurrent updates from multiple peers without conflicts.

// ✓ Good: Use useSync for shared state
const [score, setScore] = useSync('gameScore', 0);

// ✗ Bad: Regular state doesn't sync
const [score, setScore] = useState(0);

Keep Local State Local

Not everything needs to be synced. Use regular state for UI-only concerns.

// UI state - local only
const [menuOpen, setMenuOpen] = useState(false);

// Game state - synced across peers
const [playerPositions, setPlayerPositions] = useSync('positions', {});

📨 Message Handling

Use Message Types

Structure your messages with a type field for easier routing and debugging.

const { onMessage, send } = usePeer();

onMessage((message, peerId) => {
  switch (message.type) {
    case 'player-move':
      handlePlayerMove(message.data);
      break;
    case 'game-over':
      handleGameOver(message.data);
      break;
    default:
      // Handle unknown message types gracefully
      break;
  }
});

// Send structured messages
send({ type: 'player-move', data: { x, y } });

Handle Message Order

Messages may arrive out of order. Use timestamps or sequence numbers for critical events.

send({
  type: 'player-move',
  timestamp: Date.now(),
  sequence: messageSequence++,
  data: { x, y }
});

🔌 Connection Management

Handle Disconnections Gracefully

Peers can disconnect at any time. Always handle disconnection events.

const { peers, onPeerDisconnect } = usePeer();

onPeerDisconnect((peerId) => {
  // Clean up their data
  removePlayer(peerId);
  // Notify other players
  broadcast({ type: 'player-left', peerId });
});

Implement Reconnection Logic

For games, implement automatic reconnection when a peer briefly loses connection.

// Keep track of recently disconnected peers
const reconnectWindow = 30000; // 30 seconds

onPeerDisconnect((peerId) => {
  markPeerAsDisconnected(peerId);
  setTimeout(() => {
    if (!isPeerReconnected(peerId)) {
      removePlayer(peerId);
    }
  }, reconnectWindow);
});

⚡ Performance

Throttle High-Frequency Updates

For real-time games, don't send position updates on every frame. Throttle to 10-20 updates per second.

import { throttle } from 'lodash';

const sendPosition = throttle((x, y) => {
  broadcast({ type: 'player-move', data: { x, y } });
}, 50); // Max 20 updates/sec

Compress Large Payloads

For binary data or large objects, compress before sending.

import pako from 'pako';

// Compress large data
const compressed = pako.deflate(JSON.stringify(largeObject));
send({ type: 'level-data', data: compressed });

// Decompress on receive
const decompressed = JSON.parse(pako.inflate(message.data, { to: 'string' }));

Use Delta Updates

Instead of sending full state, send only what changed.

// ✗ Bad: Send entire state every time
broadcast({ type: 'state-update', data: entireGameState });

// ✓ Good: Send only changes
broadcast({ type: 'state-update', data: {
  changed: { player1: { x: 100 } }
} });

🔒 Security

Enable Encryption

Always enable encryption for sensitive data. Toolkit-P2P uses end-to-end encryption by default.

<P2PProvider
  transport="hybrid"
  config={{
    encryption: {
      enabled: true,
      key: process.env.ENCRYPTION_KEY
    }
  }}
>
  <App />
</P2PProvider>

Validate All Inputs

Never trust data from peers. Always validate message structure and values.

onMessage((message, peerId) => {
  // Validate message structure
  if (!message.type || !message.data) {
    return; // Ignore invalid messages
  }

  // Validate values
  if (message.type === 'player-move') {
    const { x, y } = message.data;
    if (typeof x !== 'number' || typeof y !== 'number') {
      return; // Ignore invalid move data
    }
    // Clamp to valid range
    handlePlayerMove(peerId, clamp(x, 0, 1000), clamp(y, 0, 1000));
  }
});

🧪 Testing

Test with Multiple Devices

P2P apps behave differently with varying numbers of peers. Test with 2, 4, and 8+ devices.

Simulate Network Conditions

Test with simulated packet loss, latency, and disconnections.

<P2PProvider
  transport="hybrid"
  config={{
    simulation: {
      latency: 100, // Add 100ms delay
      packetLoss: 0.05, // 5% packet loss
    }
  }}
>
  <App />
</P2PProvider>

Test Airplane Mode

Always test your app in airplane mode with Bluetooth enabled. This is the core value proposition of P2P apps.