CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-native-web

React Native for Web is a comprehensive compatibility library that enables React Native components and APIs to run seamlessly on web browsers using React DOM.

Pending
Overview
Eval results
Files

platform-apis.mddocs/

Platform APIs

React Native's platform APIs adapted for web environments, including device information, system services, web integrations, and cross-platform utilities.

Platform

Provides platform detection and conditional code execution with web-specific implementations.

const Platform: {
  OS: 'web';
  Version: string;
  isTesting: boolean;
  select: <T>(specifics: {web?: T, default?: T}) => T;
};

Properties:

  • OS - Always 'web' for React Native Web
  • Version - Platform version (always '0.0.0' for web)
  • isTesting - Whether running in test environment

Methods:

  • select(obj) - Select platform-specific values

Usage:

import { Platform } from "react-native-web";

// Check platform
if (Platform.OS === 'web') {
  console.log('Running on web');
}

// Platform-specific values
const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: Platform.select({
      web: 20,
      default: 0
    })
  },
  text: {
    fontSize: Platform.select({
      web: 16,
      default: 14
    })
  }
});

// Platform-specific components
const HeaderComponent = Platform.select({
  web: WebHeader,
  default: DefaultHeader
});

// Conditional rendering
function App() {
  return (
    <View>
      <Text>Hello React Native Web!</Text>
      {Platform.OS === 'web' && (
        <Text>This only shows on web</Text>
      )}
    </View>
  );
}

// Environment detection
const isProduction = !Platform.isTesting && process.env.NODE_ENV === 'production';

StatusBar

Component and API for controlling the status bar appearance and behavior. On web, this is a compatibility component that provides no-op implementations for mobile-specific status bar functionality.

const StatusBar: React.ComponentType<StatusBarProps> & {
  setBackgroundColor: (color: string, animated?: boolean) => void;
  setBarStyle: (style: 'default' | 'light-content' | 'dark-content', animated?: boolean) => void;
  setHidden: (hidden: boolean, animation?: 'none' | 'fade' | 'slide') => void;
  setNetworkActivityIndicatorVisible: (visible: boolean) => void;
  setTranslucent: (translucent: boolean) => void;
};

Web Implementation: StatusBar on web is a compatibility layer that renders null and provides no-op static methods. This ensures cross-platform compatibility for apps that use StatusBar on mobile platforms.

Component Usage

<StatusBar barStyle="dark-content" backgroundColor="#ffffff" hidden={false} />

Static Methods

// All static methods are no-ops on web but maintain compatibility
StatusBar.setBackgroundColor(color: string, animated?: boolean): void
StatusBar.setBarStyle(style: 'default' | 'light-content' | 'dark-content', animated?: boolean): void
StatusBar.setHidden(hidden: boolean, animation?: 'none' | 'fade' | 'slide'): void
StatusBar.setNetworkActivityIndicatorVisible(visible: boolean): void
StatusBar.setTranslucent(translucent: boolean): void

Usage:

import React from "react";
import { StatusBar, View, Text } from "react-native-web";

function App() {
  return (
    <View style={{ flex: 1 }}>
      {/* StatusBar component renders nothing on web but maintains compatibility */}
      <StatusBar
        barStyle="dark-content"
        backgroundColor="#ffffff"
        hidden={false}
      />
      
      <Text>App content here</Text>
    </View>
  );
}

// Static methods are no-ops on web
function setStatusBarStyle() {
  // These calls do nothing on web but don't throw errors
  StatusBar.setBarStyle('light-content');
  StatusBar.setBackgroundColor('#000000');
  StatusBar.setHidden(true, 'fade');
}

Cross-Platform Considerations:

import { Platform, StatusBar } from "react-native-web";

function ConfigureStatusBar() {
  // Only configure status bar on mobile platforms
  if (Platform.OS !== 'web') {
    StatusBar.setBarStyle('light-content');
    StatusBar.setBackgroundColor('#1a1a1a');
  }
  
  // Or use the component approach (safe on all platforms)
  return (
    <StatusBar 
      barStyle="light-content" 
      backgroundColor="#1a1a1a" 
    />
  );
}

Dimensions

Utilities for accessing screen and window dimensions with responsive design support and automatic updates.

const Dimensions: {
  get: (dimension: 'window' | 'screen') => DisplayMetrics;
  addEventListener: (type: 'change', handler: (dimensions: DimensionsValue) => void) => EventSubscription;
  removeEventListener: (type: 'change', handler: Function) => void;
  set: (initialDimensions?: DimensionsValue) => void;
};

DisplayMetrics:

{
  width: number;    // Width in CSS pixels
  height: number;   // Height in CSS pixels
  scale: number;    // Device pixel ratio
  fontScale: number; // Font scale factor (always 1 on web)
}

Dimensions:

  • window - Viewport dimensions (changes with browser resize)
  • screen - Screen dimensions (full screen size)

Usage:

import { Dimensions } from "react-native-web";

// Get dimensions
const windowDimensions = Dimensions.get('window');
const screenDimensions = Dimensions.get('screen');

console.log('Window:', windowDimensions);
// { width: 1024, height: 768, scale: 2, fontScale: 1 }

console.log('Screen:', screenDimensions);
// { width: 1920, height: 1080, scale: 2, fontScale: 1 }

// Responsive design
function ResponsiveComponent() {
  const [dimensions, setDimensions] = useState(Dimensions.get('window'));

  useEffect(() => {
    const subscription = Dimensions.addEventListener('change', ({ window }) => {
      setDimensions(window);
    });

    return () => subscription?.remove();
  }, []);

  const isTablet = dimensions.width >= 768;
  const isDesktop = dimensions.width >= 1024;

  return (
    <View style={[
      styles.container,
      isTablet && styles.tablet,
      isDesktop && styles.desktop
    ]}>
      <Text>Current width: {dimensions.width}px</Text>
      <Text>Device type: {isDesktop ? 'Desktop' : isTablet ? 'Tablet' : 'Mobile'}</Text>
    </View>
  );
}

// Breakpoint utilities
const breakpoints = {
  mobile: 0,
  tablet: 768,
  desktop: 1024,
  wide: 1200
};

function getBreakpoint(width) {
  if (width >= breakpoints.wide) return 'wide';
  if (width >= breakpoints.desktop) return 'desktop';
  if (width >= breakpoints.tablet) return 'tablet';
  return 'mobile';
}

// Layout calculations
function calculateLayout() {
  const { width, height } = Dimensions.get('window');
  const isLandscape = width > height;
  const aspectRatio = width / height;
  
  return {
    isLandscape,
    aspectRatio,
    gridColumns: Math.floor(width / 300), // 300px per column
    itemWidth: (width - 40) / 2 // 20px margin, 2 columns
  };
}

AppState

Application state management for detecting when the app becomes active, inactive, or goes to background using the Page Visibility API.

const AppState: {
  currentState: 'active' | 'background';
  isAvailable: boolean;
  addEventListener: (type: 'change' | 'memoryWarning', handler: (state: string) => void) => EventSubscription;
};

States:

  • active - App is in foreground and visible
  • background - App is in background (page is hidden)

Usage:

import { AppState } from "react-native-web";

// Get current state
console.log('Current state:', AppState.currentState);

// Listen for state changes
function AppStateExample() {
  const [appState, setAppState] = useState(AppState.currentState);

  useEffect(() => {
    const handleAppStateChange = (nextAppState) => {
      console.log('App state changed to:', nextAppState);
      setAppState(nextAppState);
    };

    const subscription = AppState.addEventListener('change', handleAppStateChange);

    return () => {
      subscription?.remove();
    };
  }, []);

  return (
    <View>
      <Text>App State: {appState}</Text>
      {appState === 'active' && (
        <Text>App is active and visible</Text>
      )}
      {appState === 'background' && (
        <Text>App is in background</Text>
      )}
    </View>
  );
}

// Pause video when app goes to background
function VideoPlayer() {
  const [isPaused, setIsPaused] = useState(false);

  useEffect(() => {
    const handleAppStateChange = (nextAppState) => {
      if (nextAppState === 'background') {
        setIsPaused(true);
      } else if (nextAppState === 'active') {
        // Optionally resume when app becomes active
        // setIsPaused(false);
      }
    };

    AppState.addEventListener('change', handleAppStateChange);
  }, []);

  return (
    <Video
      source={{ uri: 'video.mp4' }}
      paused={isPaused}
      onPress={() => setIsPaused(!isPaused)}
    />
  );
}

// Auto-save when app goes to background
function AutoSaveForm() {
  const [formData, setFormData] = useState({});

  useEffect(() => {
    const handleAppStateChange = (nextAppState) => {
      if (nextAppState === 'background') {
        // Save form data
        localStorage.setItem('formData', JSON.stringify(formData));
      }
    };

    AppState.addEventListener('change', handleAppStateChange);
  }, [formData]);

  // ... rest of component
}

PixelRatio

Utilities for working with device pixel density and converting between logical and physical pixels.

const PixelRatio: {
  get: () => number;
  getFontScale: () => number;
  getPixelSizeForLayoutSize: (layoutSize: number) => number;
  roundToNearestPixel: (layoutSize: number) => number;
};

Methods:

  • get() - Returns device pixel ratio (e.g., 2 for Retina displays)
  • getFontScale() - Returns font scale multiplier (always 1 on web)
  • getPixelSizeForLayoutSize(dp) - Convert density-independent pixels to actual pixels
  • roundToNearestPixel(dp) - Round to nearest pixel boundary

Usage:

import { PixelRatio } from "react-native-web";

// Get pixel ratio
const pixelRatio = PixelRatio.get();
console.log('Device pixel ratio:', pixelRatio); // 2 on Retina displays

// Convert logical pixels to physical pixels
const logicalSize = 100;
const physicalSize = PixelRatio.getPixelSizeForLayoutSize(logicalSize);
console.log(`${logicalSize}dp = ${physicalSize}px`);

// Round to pixel boundaries for crisp rendering
const crispSize = PixelRatio.roundToNearestPixel(100.7);
console.log('Crisp size:', crispSize); // 100.5 on 2x display

// High-DPI image selection
function HighDPIImage({ src, alt, width, height }) {
  const pixelRatio = PixelRatio.get();
  const imageSrc = pixelRatio > 1 ? `${src}@2x.png` : `${src}.png`;
  
  return (
    <img
      src={imageSrc}
      alt={alt}
      width={width}
      height={height}
      style={{
        width: PixelRatio.roundToNearestPixel(width),
        height: PixelRatio.roundToNearestPixel(height)
      }}
    />
  );
}

// Pixel-perfect borders
const styles = StyleSheet.create({
  separator: {
    height: 1 / PixelRatio.get(), // Always 1 physical pixel
    backgroundColor: '#ccc'
  },
  crispContainer: {
    width: PixelRatio.roundToNearestPixel(200.3),
    height: PixelRatio.roundToNearestPixel(150.7),
    borderWidth: 1 / PixelRatio.get()
  }
});

Keyboard

Keyboard utilities with limited web functionality. Mainly provides compatibility with React Native code.

const Keyboard: {
  dismiss: () => void;
  addListener: (eventType: string, callback: Function) => EventSubscription;
  removeListener: (eventType: string, callback: Function) => void;
  removeAllListeners: (eventType: string) => void;
  isVisible: () => boolean;
};

Usage:

import { Keyboard } from "react-native-web";

// Dismiss keyboard (blurs active input)
function DismissKeyboard() {
  const handleSubmit = () => {
    // Process form
    Keyboard.dismiss();
  };

  return (
    <View>
      <TextInput placeholder="Type here..." />
      <Button title="Submit" onPress={handleSubmit} />
    </View>
  );
}

// Note: Keyboard event listeners are no-ops on web
// Use standard DOM events or Visual Viewport API for keyboard detection
function WebKeyboardDetection() {
  const [keyboardVisible, setKeyboardVisible] = useState(false);

  useEffect(() => {
    function handleResize() {
      // Simple heuristic: significant height reduction might indicate keyboard
      const currentHeight = window.innerHeight;
      const isKeyboardVisible = currentHeight < window.screen.height * 0.8;
      setKeyboardVisible(isKeyboardVisible);
    }

    // Better approach: Use Visual Viewport API if available
    if (window.visualViewport) {
      window.visualViewport.addEventListener('resize', handleResize);
      return () => window.visualViewport.removeEventListener('resize', handleResize);
    } else {
      window.addEventListener('resize', handleResize);
      return () => window.removeEventListener('resize', handleResize);
    }
  }, []);

  return (
    <View style={{ paddingBottom: keyboardVisible ? 20 : 0 }}>
      <Text>Keyboard is {keyboardVisible ? 'visible' : 'hidden'}</Text>
    </View>
  );
}

BackHandler

Android back button handler. On web, this is a no-op for React Native compatibility.

const BackHandler: {
  addEventListener: () => EventSubscription;
  removeEventListener: () => void;
  exitApp: () => void;
};

Usage:

import { BackHandler, Platform } from "react-native-web";

// Cross-platform back handling
function NavigationHandler() {
  useEffect(() => {
    if (Platform.OS !== 'web') {
      const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
        // Handle back button on Android
        return true; // Prevent default behavior
      });

      return () => backHandler.remove();
    }
  }, []);

  // For web, use browser history or custom navigation
  return <YourComponent />;
}

// Web-specific back button handling
function WebBackButton() {
  const navigate = useNavigate(); // React Router or similar

  const handleBack = () => {
    if (Platform.OS === 'web') {
      // Use browser history
      window.history.back();
      // Or programmatic navigation
      // navigate(-1);
    } else {
      // Let BackHandler handle it on mobile
    }
  };

  return (
    <TouchableOpacity onPress={handleBack}>
      <Text>← Back</Text>
    </TouchableOpacity>
  );
}

DeviceEventEmitter

Global event emitter for cross-component communication and device events.

const DeviceEventEmitter: {
  addListener: (eventType: string, callback: Function) => EventSubscription;
  removeListener: (eventType: string, callback: Function) => void;
  emit: (eventType: string, data?: any) => void;
  removeAllListeners: (eventType?: string) => void;
};

Usage:

import { DeviceEventEmitter } from "react-native-web";

// Global event communication
function EventEmitterExample() {
  useEffect(() => {
    const subscription = DeviceEventEmitter.addListener('customEvent', (data) => {
      console.log('Received event:', data);
    });

    return () => subscription.remove();
  }, []);

  const emitEvent = () => {
    DeviceEventEmitter.emit('customEvent', { 
      message: 'Hello from emitter!',
      timestamp: Date.now()
    });
  };

  return (
    <Button title="Emit Event" onPress={emitEvent} />
  );
}

// Cross-component notifications
class NotificationService {
  static notify(type, message) {
    DeviceEventEmitter.emit('notification', { type, message });
  }

  static success(message) {
    this.notify('success', message);
  }

  static error(message) {
    this.notify('error', message);
  }
}

function NotificationListener() {
  const [notification, setNotification] = useState(null);

  useEffect(() => {
    const subscription = DeviceEventEmitter.addListener('notification', (data) => {
      setNotification(data);
      setTimeout(() => setNotification(null), 3000);
    });

    return () => subscription.remove();
  }, []);

  if (!notification) return null;

  return (
    <View style={[
      styles.notification,
      { backgroundColor: notification.type === 'error' ? 'red' : 'green' }
    ]}>
      <Text style={styles.notificationText}>{notification.message}</Text>
    </View>
  );
}

Types

interface DisplayMetrics {
  width: number;
  height: number;
  scale: number;
  fontScale: number;
}

interface DimensionsValue {
  window: DisplayMetrics;
  screen: DisplayMetrics;
}

interface EventSubscription {
  remove: () => void;
}

interface PlatformStatic {
  OS: 'web';
  Version: string;
  isTesting: boolean;
  select: <T>(specifics: {web?: T, default?: T}) => T;
}

interface DimensionsStatic {
  get: (dimension: 'window' | 'screen') => DisplayMetrics;
  addEventListener: (type: 'change', handler: (dimensions: DimensionsValue) => void) => EventSubscription;
  removeEventListener: (type: 'change', handler: Function) => void;
  set: (initialDimensions?: DimensionsValue) => void;
}

interface AppStateStatic {
  currentState: 'active' | 'background';
  isAvailable: boolean;
  addEventListener: (type: 'change' | 'memoryWarning', handler: (state: string) => void) => EventSubscription | undefined;
}

interface PixelRatioStatic {
  get: () => number;
  getFontScale: () => number;
  getPixelSizeForLayoutSize: (layoutSize: number) => number;
  roundToNearestPixel: (layoutSize: number) => number;
}

interface KeyboardStatic {
  dismiss: () => void;
  addListener: (eventType: string, callback: Function) => EventSubscription;
  removeListener: (eventType: string, callback: Function) => void;
  removeAllListeners: (eventType?: string) => void;
  isVisible: () => boolean;
}

interface BackHandlerStatic {
  addEventListener: (type: 'hardwareBackPress', handler: () => boolean) => EventSubscription;
  removeEventListener: (type: 'hardwareBackPress', handler: Function) => void;
  exitApp: () => void;
}

interface StatusBarProps {
  animated?: boolean;
  backgroundColor?: string;
  barStyle?: 'default' | 'light-content' | 'dark-content';
  hidden?: boolean;
  networkActivityIndicatorVisible?: boolean;
  showHideTransition?: 'fade' | 'slide';
  translucent?: boolean;
}

Install with Tessl CLI

npx tessl i tessl/npm-react-native-web

docs

accessibility.md

animation.md

core-utilities.md

form-controls.md

hooks.md

index.md

interactive-components.md

layout-components.md

list-components.md

media-components.md

platform-apis.md

stylesheet.md

system-integration.md

text-input.md

tile.json