I needed to test UDP broadcasting from a React Native (Expo) app running on a mobile device over LAN. Here’s a quick summary of how I did it, from preview setup to packet verification using socat.
Required Dependencies
These are the libraries I used:
expo-network– for getting the device’s local IP address. This is not required for udp broadcastreact-native-udp– to send UDP packets
Configure metro.config.js
Because react-native-udp is a Node.js core module polyfill, you need to update your Metro bundler config to support shimming:
metro.config.js
const { getDefaultConfig } = require("@expo/metro-config");
const config = getDefaultConfig(__dirname);
config.resolver.extraNodeModules = {
...config.resolver.extraNodeModules,
dgram: require.resolve("react-native-udp"),
};
module.exports = config;Create a Basic UDP Broadcast Sender
Here’s a minimal PocScreen.tsx that sends a UDP broadcast:
PocScreen.tsx
import React, { useEffect, useRef, useState } from "react";
import {
Text,
Button,
ScrollView,
StyleSheet,
SafeAreaView,
} from "react-native";
import * as Network from "expo-network";
import dgram from "react-native-udp";
type UdpSocket = ReturnType<typeof dgram.createSocket>;
const PORT = 12345;
const getBroadcastAddress = (ip: string): string =>
ip.split(".").slice(0, 3).join(".") + ".255";
const PocUdpScreen: React.FC = () => {
const [ip, setIp] = useState<string | null>(null);
const [broadcastIp, setBroadcastIp] = useState<string>("192.168.0.255");
const [messages, setMessages] = useState<string[]>([]);
const socketRef = useRef<UdpSocket | null>(null);
const ipRef = useRef<string | null>(null);
useEffect(() => {
let isMounted = true;
(async () => {
const ipAddr = await Network.getIpAddressAsync();
if (!isMounted) {
return;
}
setIp(ipAddr);
ipRef.current = ipAddr;
setBroadcastIp(getBroadcastAddress(ipAddr));
})();
const socket = dgram.createSocket({ type: "udp4" });
socketRef.current = socket;
socket.bind(PORT);
socket.once("listening", () => {
socket.setBroadcast(true);
});
socket.on("message", (msg, rinfo) => {
const text = new TextDecoder().decode(msg);
const from =
rinfo.address === ipRef.current
? `${rinfo.address} (you)`
: rinfo.address;
setMessages((prev) => [...prev, `${from}: ${text}`]);
});
return () => {
isMounted = false;
socket.close();
};
}, []);
const handleSend = () => {
if (!ip || !socketRef.current) {
return;
}
const message = `📡 Hello from ${ip} at ${new Date().toISOString()}`;
const buffer = new TextEncoder().encode(message);
socketRef.current.send(
buffer,
0,
buffer.length,
PORT,
broadcastIp,
(err) => {
if (err) {
console.error("UDP send error:", err);
} else {
console.log(`✅ Sent to ${broadcastIp}:${PORT}`);
}
}
);
};
return (
<SafeAreaView style={styles.container}>
<Text style={styles.title}>📡 UDP Broadcast PoC</Text>
<Text>Your IP: {ip ?? "Loading..."}</Text>
<Text>
Broadcast: {broadcastIp}:{PORT}
</Text>
<Button title="Send UDP" onPress={handleSend} />
<Text style={styles.subtitle}>Received Messages</Text>
<ScrollView style={styles.scroll}>
{messages.map((msg, i) => (
<Text key={i} style={styles.message}>
{msg}
</Text>
))}
</ScrollView>
</SafeAreaView>
);
};
export default PocUdpScreen;
const styles = StyleSheet.create({
container: { flex: 1, padding: 16 },
title: { fontSize: 20, fontWeight: "bold", marginBottom: 8 },
subtitle: { fontSize: 16, marginTop: 20, fontWeight: "600" },
scroll: { marginTop: 10, maxHeight: 300 },
message: { marginBottom: 6, fontSize: 14 },
});To test this screen, I deployed it as a preview build on my phone using expo run:android. It worked well on a real device.
Install socat on macOS
To verify the packets on your Mac, install socat:
brew install socatStart Listening for UDP Packets
In a terminal window:
socat -v -u UDP-RECV:12345 STDOUTI successfully received outputs
📡 Hello from 192.168.0.16 at 2025-06-25T22:58:05.880Z
📡 Hello from 192.168.0.16 at 2025-06-25T22:58:08.460ZSummary
- UDP broadcasting from Expo works with react-native-udp
- socat successfully receives and prints UDP messages in real time