Repository navigation
Scanning
Find the services other devices advertise on the local network, and read what they resolve to.
- Scanning for services
- Multiple scans
- Resolved services
- Subtypes
- Choosing a network interface
- Resolving a single service
- Listing service types
- Common service types
A scan runs in two steps: a service is found (only its name is known), then resolved (host, port, addresses and TXT records are known).
import Zeroconf from 'react-native-zeroconf'
const zeroconf = new Zeroconf()
zeroconf.on('start', () => console.log('Scan started'))
zeroconf.on('found', name => console.log('Found', name))
zeroconf.on('resolved', service => console.log('Resolved', service.name, service.addresses))
zeroconf.on('remove', name => console.log('Gone', name))
zeroconf.on('error', error => console.warn(error.domain, error.code, error.message))
zeroconf.scan({ type: 'http', protocol: 'tcp', domain: 'local.' })All scan options are optional: scan() alone browses _http._tcp. on local..
- Calling
scan()again on the same instance replaces that instance's scan and clears itsgetServices(). To browse several types at once, use one instance per scan (see Multiple scans). -
stop()ends the instance's scan. On Android it stops the implementation the last scan used. - Slow devices can take a while to resolve. Raise
resolveTimeout(seconds, default5); a timed out resolve is retried once before anerrorwith code'TIMEOUT'.
zeroconf.scan({ type: 'ipp', resolveTimeout: 15 }) // slow printers
zeroconf.scan({ type: 'pdl-datastream', implType: 'DNSSD' }) // Android: embedded mDNSRespondergetServices() returns the services of the current scan keyed by name. The update event fires whenever that list changes, which makes it a convenient single listener for UIs:
zeroconf.on('update', () => {
const services = Object.values(zeroconf.getServices())
render(services)
})Entries that are found but not resolved yet only have a
name. Check forportoraddressesbefore using them.
Each Zeroconf instance runs its own scan, so several scans can run at the same time. Each instance only receives its own scan's events (start, stop, found, resolved, remove, update and scan errors), and getServices() only holds its own results.
const printers = new Zeroconf()
const speakers = new Zeroconf()
printers.on('resolved', service => console.log('printer', service.name))
speakers.on('resolved', service => console.log('speaker', service.name))
printers.scan({ type: 'ipp' })
speakers.scan({ type: 'raop' })
// Later
printers.stop()
speakers.stop()- An instance that never called
scan()still receives the events of every scan, as in earlier versions. - Publishing events (
published,unpublished) and errors not tied to a scan go to every instance. - On Android,
NSDandDNSSDscans can run at the same time, for exampleprinters.scan({ type: 'ipp', implType: 'DNSSD' })next to anNSDscan. - On iOS, every scanned type must be listed in
NSBonjourServices(here_ipp._tcpand_raop._tcp). - With the hook, each
useZeroconfcall runs its own scan, so several components can scan different types at once.
{
name: 'Xerox Printer',
fullName: 'XeroxPrinter._http._tcp.local.',
host: 'XeroxPrinter.local.',
port: 8080,
addresses: ['192.168.1.23', 'fe80::aebc:123:ffff:abcd'], // IPv4 first
ipv4: ['192.168.1.23'],
ipv6: ['fe80::aebc:123:ffff:abcd'],
txt: { path: '/status', color: 'yes' },
}To connect, prefer service.ipv4[0] (or service.addresses[0], which is IPv4 when one exists):
zeroconf.on('resolved', service => {
const address = service.ipv4[0] ?? service.addresses[0]
if (address) fetch(`http://${address}:${service.port}${service.txt.path ?? '/'}`)
})A resolved service is emitted as resolved again when its addresses or TXT record change, so update your list by service.name rather than appending:
| Platform |
resolved again on changes |
|---|---|
| iOS | Yes |
Android NSD
|
Android 14+ |
Android DNSSD
|
No, once per address found |
| Windows | Yes, when the TXT record or the host and port change |
On Android with
NSD,hostis the mDNS host name only on Android 16+. Earlier versions usually give the IP address inhost. UseimplType: 'DNSSD'if you need the host name there. See Android Implementations.
Services can be registered with subtypes (_printer._sub._ipp._tcp), for example to tell color printers apart. Scan a subtype to only find those services:
zeroconf.scan({ type: 'ipp', subtype: 'printer' })subtype takes the label with or without the leading underscore. Publish with subtypes using subtypes: ['printer'] (see Publishing). Subtypes work on iOS and with both Android implementations.
By default, scans use every network interface. Pass networkInterface to use one, by its system name:
zeroconf.scan({ type: 'http', networkInterface: Platform.OS === 'ios' ? 'en0' : 'wlan0' })- Common names:
en0(iOS Wi-Fi),wlan0(Android Wi-Fi),eth0(Android Ethernet). - An interface that doesn't exist emits an
errorwith code'UNKNOWN_INTERFACE'. - Android
NSDneeds Android 13 or later for it, earlier versions emit anerrorwith code'UNSUPPORTED'.DNSSDsupports every version.
publishService() and resolveService() take the same option.
To reach a service you found before, resolve it by name instead of scanning again:
try {
const printer = await zeroconf.resolveService({ name: 'Office Printer', type: 'ipp' })
console.log(printer.ipv4[0], printer.port)
} catch (error) {
if (error.code === 'TIMEOUT') {
// Not on the network right now
}
}It resolves once with a Service and doesn't emit events. timeout (seconds, default 5) sets how long to wait before rejecting with code 'TIMEOUT'.
mDNS only finds services of a type you ask for. To see which types are advertised on the network, list them first:
const zeroconf = new Zeroconf()
zeroconf.on('typeFound', ({ type, protocol }) => console.log(`_${type}._${protocol}`))
zeroconf.on('typeRemove', ({ type, protocol }) => console.log(`gone: _${type}._${protocol}`))
zeroconf.scanServiceTypes()
// Later
zeroconf.getServiceTypes() // [{ type: 'http', protocol: 'tcp' }, { type: 'ipp', protocol: 'tcp' }]Then scan each type you are interested in, with one instance per type or useZeroconf per type (see Multiple scans). Service types are not resolved, found and resolved are not emitted while listing them. In React, use useServiceTypes.
| Platform | Support |
|---|---|
| Android | Yes. With NSD (the default), services that other apps publish on the same phone are left out, this app's own are included. DNSSD includes them all |
| Windows | Yes. Services that other apps publish on the same computer are left out, this app's own are included |
| iOS | Requires the multicast entitlement (com.apple.developer.networking.multicast), which apps request from Apple. Without it, listing fails with error -65555. Apple's Local Network Privacy FAQ lists browsing for service types among the operations that need it. Apps without the entitlement scan a known list of types, declared in NSBonjourServices
|
On Android, NsdManager can't list service types (Android 14 and later read _services._dns-sd._udp as a subtype), so with NSD the library queries the network itself: it sends the mDNS query from its own socket and repeats it every 10 seconds, a type is removed after 35 seconds without an answer.
| Service | type |
protocol |
|---|---|---|
| HTTP | http |
tcp |
| HTTPS | https |
tcp |
| Printer (raw) | pdl-datastream |
tcp |
| Printer (IPP) | ipp |
tcp |
| SSH | ssh |
tcp |
| FTP | ftp |
tcp |
| AirPlay | airplay |
tcp |
| Chromecast | googlecast |
tcp |
Remember: on iOS each type you use must be in NSBonjourServices (for example _ipp._tcp).
Next: Publishing
Documentation for react-native-zeroconf 0.17. Found a mistake? Open an issue. | Home | API Reference | Troubleshooting