or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

app-utilities.mderror-handling.mdevent-communication.mdindex.mdnative-modules.mdnative-views.mdpermissions.mdplatform-utilities.mdshared-memory.mduuid-generation.md
tile.json

native-views.mddocs/

Native View Components

Integration system for React Native components backed by native view managers, with automatic prototype binding and lifecycle management.

Capabilities

Native View Manager Loading

Function for creating React Native components from Expo native view managers with automatic prototype binding.

/**
 * A drop-in replacement for requireNativeComponent that works with Expo modules
 * Creates React Native components backed by native view managers
 * @param moduleName - Name of the native module containing the view manager
 * @param viewName - Optional specific view name within the module
 * @returns React component type that can be used in JSX
 */
function requireNativeViewManager<P>(
  moduleName: string,
  viewName?: string
): ComponentType<P>;

Usage Examples:

import { requireNativeViewManager } from "expo-modules-core";
import { ViewProps } from "react-native";

// Basic native view component
interface MapViewProps extends ViewProps {
  region: {
    latitude: number;
    longitude: number;
    latitudeDelta: number;
    longitudeDelta: number;
  };
  onRegionChange?: (region: any) => void;
}

// Create native view component
const MapView = requireNativeViewManager<MapViewProps>("ExpoMapView");

// Use in component
function MapScreen() {
  const [region, setRegion] = useState({
    latitude: 37.78825,
    longitude: -122.4324,
    latitudeDelta: 0.0922,
    longitudeDelta: 0.0421,
  });
  
  return (
    <MapView
      style={{ flex: 1 }}
      region={region}
      onRegionChange={setRegion}
    />
  );
}

// Multiple views from same module
const CameraView = requireNativeViewManager<CameraViewProps>("ExpoCamera", "CameraView");
const CameraPreview = requireNativeViewManager<CameraPreviewProps>("ExpoCamera", "PreviewView");

function CameraScreen() {
  return (
    <div>
      <CameraView style={{ flex: 1 }} />
      <CameraPreview style={{ height: 100 }} />
    </div>
  );
}

Native View Prototypes and Method Binding

The view manager system automatically binds native methods to component instances through prototypes.

import { requireNativeViewManager } from "expo-modules-core";
import { useRef } from "react";

// Native view with callable methods
interface VideoViewProps extends ViewProps {
  source: { uri: string };
  onStatusUpdate?: (status: VideoStatus) => void;
}

// Methods automatically bound from native view prototype
interface VideoViewMethods {
  playAsync(): Promise<void>;
  pauseAsync(): Promise<void>;
  seekToAsync(positionMillis: number): Promise<void>;
  getStatusAsync(): Promise<VideoStatus>;
}

const VideoView = requireNativeViewManager<VideoViewProps>("ExpoVideo");

function VideoPlayer({ source }: { source: { uri: string } }) {
  // Ref will have native methods available
  const videoRef = useRef<VideoViewMethods>(null);
  
  const handlePlay = async () => {
    if (videoRef.current) {
      await videoRef.current.playAsync();
    }
  };
  
  const handlePause = async () => {
    if (videoRef.current) {
      await videoRef.current.pauseAsync();
    }
  };
  
  const handleSeek = async (position: number) => {
    if (videoRef.current) {
      await videoRef.current.seekToAsync(position);
    }
  };
  
  return (
    <div>
      <VideoView
        ref={videoRef}
        source={source}
        style={{ width: 300, height: 200 }}
        onStatusUpdate={(status) => console.log("Video status:", status)}
      />
      <div>
        <button onClick={handlePlay}>Play</button>
        <button onClick={handlePause}>Pause</button>
        <button onClick={() => handleSeek(30000)}>Seek to 30s</button>
      </div>
    </div>
  );
}

View Config and Registration

Native views use static view configurations for React Native integration with proper event handling.

// Example view config structure (handled internally)
interface ViewConfig {
  validAttributes: Record<string, any>;
  directEventTypes: Record<string, { registrationName: string }>;
}

// Internal view registration process
function internalViewRegistration(moduleName: string, viewName?: string) {
  // Get view config from native module
  const expoViewConfig = globalThis.expo?.getViewConfig(moduleName, viewName);
  
  // Create React Native component with proper configuration
  return {
    uiViewClassName: `ViewManagerAdapter_${moduleName}_${viewName || 'default'}`,
    validAttributes: expoViewConfig?.validAttributes || {},
    directEventTypes: expoViewConfig?.directEventTypes || {},
  };
}

Usage Examples:

import { requireNativeViewManager } from "expo-modules-core";

// View with custom events
interface CustomViewProps extends ViewProps {
  customProperty: string;
  onCustomEvent?: (event: { nativeEvent: { data: string } }) => void;
  onAnotherEvent?: (event: { nativeEvent: { value: number } }) => void;
}

const CustomView = requireNativeViewManager<CustomViewProps>("MyCustomModule");

function CustomComponent() {
  return (
    <CustomView
      customProperty="test"
      onCustomEvent={(event) => {
        console.log("Custom event:", event.nativeEvent.data);
      }}
      onAnotherEvent={(event) => {
        console.log("Another event:", event.nativeEvent.value);
      }}
      style={{ width: 200, height: 100 }}
    />
  );
}

Error Handling and Fallbacks

Best practices for handling native view loading errors and providing fallbacks.

import { requireNativeViewManager } from "expo-modules-core";
import { Platform } from "expo-modules-core";

// Conditional view loading with fallbacks
function createNativeView<T>(moduleName: string, fallbackComponent?: ComponentType<T>) {
  try {
    return requireNativeViewManager<T>(moduleName);
  } catch (error) {
    console.warn(`Native view ${moduleName} not available:`, error);
    return fallbackComponent || (() => <div>Native view not available</div>);
  }
}

// Platform-specific views
const PlatformSpecificView = Platform.select({
  native: () => requireNativeViewManager<ViewProps>("NativeOnlyView"),
  web: () => ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
  default: () => () => <div>Unsupported platform</div>,
})();

// Graceful degradation
interface MediaPlayerProps extends ViewProps {
  source: { uri: string };
  controls?: boolean;
}

const NativeMediaPlayer = (() => {
  try {
    return requireNativeViewManager<MediaPlayerProps>("ExpoMediaPlayer");
  } catch {
    // Fallback to HTML5 video on web
    return ({ source, style, ...props }: MediaPlayerProps) => (
      <video
        src={source.uri}
        controls
        style={style}
        {...props}
      />
    );
  }
})();

function MediaComponent({ source }: { source: { uri: string } }) {
  return (
    <NativeMediaPlayer
      source={source}
      controls={true}
      style={{ width: '100%', height: 200 }}
    />
  );
}

Advanced Native View Patterns

Complex usage patterns for native view integration.

import { requireNativeViewManager } from "expo-modules-core";
import { forwardRef, useImperativeHandle, useRef } from "react";

// Forwarding refs with native methods
interface WebViewProps extends ViewProps {
  source: { uri: string };
  onLoadEnd?: () => void;
}

interface WebViewMethods {
  goBack(): void;
  goForward(): void;
  reload(): void;
  evaluateJavaScript(script: string): Promise<string>;
}

const NativeWebView = requireNativeViewManager<WebViewProps>("ExpoWebView");

const WebView = forwardRef<WebViewMethods, WebViewProps>((props, ref) => {
  const nativeRef = useRef<WebViewMethods>(null);
  
  useImperativeHandle(ref, () => ({
    goBack: () => nativeRef.current?.goBack(),
    goForward: () => nativeRef.current?.goForward(), 
    reload: () => nativeRef.current?.reload(),
    evaluateJavaScript: (script: string) => 
      nativeRef.current?.evaluateJavaScript(script) || Promise.resolve(''),
  }));
  
  return <NativeWebView ref={nativeRef} {...props} />;
});

// Usage with ref
function WebBrowser() {
  const webViewRef = useRef<WebViewMethods>(null);
  
  const handleGoBack = () => {
    webViewRef.current?.goBack();  
  };
  
  const handleRunScript = async () => {
    const result = await webViewRef.current?.evaluateJavaScript('document.title');
    console.log('Page title:', result);
  };
  
  return (
    <div>
      <WebView
        ref={webViewRef}
        source={{ uri: 'https://example.com' }}
        style={{ flex: 1 }}
      />
      <div>
        <button onClick={handleGoBack}>Back</button>
        <button onClick={handleRunScript}>Get Title</button>
      </div>
    </div>
  );
}

// Component composition with multiple native views
function ComplexMediaView() {
  const audioRef = useRef(null);
  const videoRef = useRef(null);
  
  const AudioPlayer = requireNativeViewManager("ExpoAudio");
  const VideoPlayer = requireNativeViewManager("ExpoVideo");
  const Visualizer = requireNativeViewManager("ExpoAudioVisualizer");
  
  return (
    <div style={{ flex: 1 }}>
      <VideoPlayer
        ref={videoRef}
        style={{ flex: 1 }}
        source={{ uri: 'video.mp4' }}
      />
      <div style={{ flexDirection: 'row' }}>
        <AudioPlayer
          ref={audioRef}
          style={{ flex: 1, height: 60 }}
          source={{ uri: 'audio.mp3' }}
        />
        <Visualizer
          style={{ flex: 1, height: 60 }}
          audioSource={audioRef}
        />
      </div>
    </div>
  );
}

Types

/**
 * React component type that can be used in JSX
 */
type ComponentType<P = {}> = React.ComponentType<P>;

/**
 * Base props that all React Native views inherit
 */
interface ViewProps {
  style?: any;
  testID?: string;
  accessibilityLabel?: string;
  [key: string]: any;
}

/**
 * Native methods interface that can be extended for specific views
 */
interface NativeMethods {
  focus?: () => void;
  blur?: () => void;
  measure?: (callback: (x: number, y: number, width: number, height: number, pageX: number, pageY: number) => void) => void;
}