Expo module for controlling AirPlay and audio output devices on iOS.
⚠️ Platform Support: This package is iOS-only and will not work on Android, web, or any other platform.
- Get current audio route information (including AirPlay detection)
- Show native AirPlay device picker
- Listen to audio route change events
Returns information about the currently active audio route, including whether it's an AirPlay device.
const result = await LazerExpoAirplay.getCurrentRoute();
if (result.success) {
console.log("Current audio route:", result.data);
// result.data: { route_id: string, route_name: string, port_type: string, is_airplay: boolean }
} else {
console.log("No audio route available:", result.error);
}
Shows the native iOS AirPlay device picker.
const result = await LazerExpoAirplay.show();
if (result.success) {
console.log("AirPlay picker shown");
}
A React hook that provides the current audio route and a function to refresh it. This hook automatically updates when the route changes.
import { useCurrentRoute } from "@lazer/expo-airplay";
function MyComponent() {
const { route, refresh } = useCurrentRoute();
return (
<View>
<Text>Current Route: {route?.route_name}</Text>
<Text>Is AirPlay: {route?.is_airplay ? "Yes" : "No"}</Text>
<Button title="Refresh Route" onPress={refresh} />
</View>
);
}
The hook returns an object with:
route
: The currentAirplayRoute
ornull
while loadingrefresh
: A function to manually refresh the current route
Fired when audio route changes occur (switching between speakers, headphones, Bluetooth, AirPlay, etc.).
import { useEvent } from "expo";
const onRouteChange = useEvent(LazerExpoAirplay, "onRouteChange");
useEffect(() => {
if (onRouteChange) {
console.log("Route changed:", onRouteChange);
// { current_route: AirplayRoute, state: ConnectionState }
// Check if it's an AirPlay device
if (onRouteChange.current_route?.is_airplay) {
console.log(
"AirPlay device connected:",
onRouteChange.current_route.route_name,
);
}
}
}, [onRouteChange]);
export type AirplayRoute = {
route_id: string; // Unique identifier for the route (e.g., "Speaker", "AirPlay-123")
route_name: string; // Display name of the route (e.g., "Speaker", "Living Room TV")
port_type: string; // iOS port type (e.g., "Speaker", "AirPlay")
is_airplay: boolean; // Whether the route is an AirPlay device
};
export type ConnectionState =
| "device_available"
| "device_unavailable"
| "category_changed"
| "override"
| "wake_from_sleep"
| "no_suitable_route"
| "configuration_changed"
| "unknown";
For managed Expo projects, please follow the installation instructions in the API documentation for the latest stable release. If you follow the link and there is no documentation available then this library is not yet usable within managed projects — it is likely to be included in an upcoming Expo SDK release.
Note: This package is iOS-only and will not work on Android or web platforms.
For bare React Native projects, you must ensure that you have
installed and configured the expo
package
before continuing.
npm install @lazer/expo-airplay
Run npx pod-install
after installing the npm package.
Note: This package is iOS-only and does not require any Android configuration.
import React, { useEffect, useState } from "react";
import { Button, Text, View } from "react-native";
import { useEvent } from "expo";
import LazerExpoAirplay, { AirplayRoute } from "@lazer/expo-airplay";
export default function AirPlayExample() {
const [currentRoute, setCurrentRoute] = useState<AirplayRoute | null>(null);
const onRouteChange = useEvent(LazerExpoAirplay, "onRouteChange");
useEffect(() => {
loadCurrentRoute();
}, []);
useEffect(() => {
if (onRouteChange) {
console.log("Audio route changed:", onRouteChange);
setCurrentRoute(onRouteChange.current_route);
}
}, [onRouteChange]);
const loadCurrentRoute = async () => {
const result = await LazerExpoAirplay.getCurrentRoute();
if (result.success) {
setCurrentRoute(result.data);
}
};
const showAirPlayPicker = async () => {
await LazerExpoAirplay.show();
};
return (
<View style={{ padding: 20 }}>
<Text>Current Audio Route:</Text>
<Text>{currentRoute?.route_name || "Loading..."}</Text>
<Text>Type: {currentRoute?.port_type}</Text>
<Text>AirPlay: {currentRoute?.is_airplay ? "Yes" : "No"}</Text>
<Button title="Show AirPlay Picker" onPress={showAirPlayPicker} />
<Button title="Refresh Route" onPress={loadCurrentRoute} />
</View>
);
}
Contributions are very welcome! Please refer to guidelines described in the contributing guide.
To start the development environment:
- Make sure you have Bun installed
- Run the development script:
bun run dev
This will:
- Build the module
- Start the iOS development environment
For Android development, you can run:
bun run dev:android