@pncl/react-native-webview
v1.0.1
Published
A React Native WebView wrapper that auto-requests native camera/microphone permissions and overrides navigator.permissions.query so web apps see the correct grant state.
Downloads
287
Readme
@pncl/react-native-webview
A WebView component for React Native, designed to maximize Pencil Spaces integration. It ensures that audio and video work seamlessly inside the WebView with a simple one-prop integration on both iOS and Android.
Why this exists
Embedding Pencil Spaces (or any app that uses camera and microphone) inside a React Native WebView requires two things to go right:
- The device must grant camera and microphone permissions to the native app
- The web app must be told that those permissions are already granted
Without (2), the web app calls navigator.permissions.query() and receives 'prompt' or 'denied' — even after the user has already accepted the native permission dialogs. This breaks audio/video flows entirely.
@pncl/react-native-webview handles both steps automatically.
Installation
npm install @pncl/react-native-webviewreact-native-webview is included as a dependency and installed automatically. No config plugins or manual linking required.
Permissions requested on mount
When the WebView mounts, it calls the required requestPermissions prop to request camera and microphone permissions. This is consistent on both iOS and Android — you supply the callback using whichever permissions library your project already uses.
| Permission | iOS | Android |
|---|---|---|
| Camera | Requested via requestPermissions prop | Requested via requestPermissions prop |
| Microphone | Requested via requestPermissions prop | Requested via requestPermissions prop |
Your app's
Info.plist(iOS) andAndroidManifest.xml(Android) must still declareNSCameraUsageDescription,NSMicrophoneUsageDescription, and the corresponding Android<uses-permission>entries. These are standard requirements for any app that uses camera or microphone.
Usage
With Expo (expo-camera + expo-av)
import WebView from '@pncl/react-native-webview';
import { Camera } from 'expo-camera';
import { Audio } from 'expo-av';
async function requestPermissions() {
const cam = await Camera.requestCameraPermissionsAsync();
const mic = await Audio.requestPermissionsAsync();
return {
camera: cam.status === 'granted',
microphone: mic.status === 'granted',
};
}
export default function SpaceScreen({ url }: { url: string }) {
return (
<WebView
source={{ uri: url }}
style={{ flex: 1 }}
requestPermissions={requestPermissions}
/>
);
}With react-native-permissions
import { Platform } from 'react-native';
import WebView from '@pncl/react-native-webview';
import { request, PERMISSIONS, RESULTS } from 'react-native-permissions';
async function requestPermissions() {
const [cam, mic] = await Promise.all([
request(Platform.select({
ios: PERMISSIONS.IOS.CAMERA,
android: PERMISSIONS.ANDROID.CAMERA,
})),
request(Platform.select({
ios: PERMISSIONS.IOS.MICROPHONE,
android: PERMISSIONS.ANDROID.RECORD_AUDIO,
})),
]);
return {
camera: cam === RESULTS.GRANTED,
microphone: mic === RESULTS.GRANTED,
};
}
export default function SpaceScreen({ url }: { url: string }) {
return (
<WebView
source={{ uri: url }}
style={{ flex: 1 }}
requestPermissions={requestPermissions}
/>
);
}Named import
Both default and named imports work:
import WebView from '@pncl/react-native-webview';
// or
import { WebView } from '@pncl/react-native-webview';Props
@pncl/react-native-webview's WebView accepts all props from react-native-webview's WebView, plus the following:
| Prop | Type | Default | Description |
|---|---|---|---|
| requestPermissions | () => Promise<{ camera: boolean; microphone: boolean }> | required | Callback to request native camera/microphone permissions on mount. Called on both iOS and Android. Use expo-camera/expo-av or react-native-permissions to implement — see examples above. |
| permissionsOverride | boolean | true | Controls whether the WebView receives the correct permission state from the native layer. Disable only if you are managing navigator.permissions yourself. |
Forwarding refs
Refs are forwarded to the underlying WebView instance, so you can still call .reload(), .goBack(), etc.
import { useRef } from 'react';
import WebView from '@pncl/react-native-webview';
import type { WebView as WebViewRef } from 'react-native-webview';
async function requestPermissions() {
// ... your permissions logic
return { camera: true, microphone: true };
}
export default function SpaceScreen({ url }: { url: string }) {
const ref = useRef<WebViewRef>(null);
return (
<WebView
ref={ref}
source={{ uri: url }}
style={{ flex: 1 }}
requestPermissions={requestPermissions}
/>
);
}License
MIT
