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/secCompress 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.