or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

devtools.mddom-components.mdindex.mdnative-modules.mdutilities.mdwinter-polyfills.md
tile.json

dom-components.mddocs/

DOM Components

The Expo DOM component system enables React Native applications to render DOM components within WebView contexts. This system provides a bridge between React Native and web technologies, allowing developers to create hybrid components that can access DOM APIs while integrating seamlessly with React Native applications.

Capabilities

DOM Component Registration

Register a React component as a DOM component that runs within a WebView context.

/**
 * Registers a React component to run as a DOM component within a WebView
 * @param AppModule - React component to register as the DOM component root
 */
function registerDOMComponent(AppModule: any): void;

DOM Imperative Handle

Create imperative handles for DOM components that can be called from the React Native side.

/**
 * React hook for creating imperative handles in DOM components
 * @param ref - React ref to bind the handle to
 * @param init - Function that creates the imperative handle object
 * @param deps - Dependency array for recreating the handle
 */
function useDOMImperativeHandle<T extends DOMImperativeFactory>(
  ref: Ref<T>,
  init: () => T,
  deps?: DependencyList
): void;

DOM Environment Detection

Utility to detect if code is running in a DOM component context.

/**
 * Constant indicating if the current environment is a DOM component
 */
const IS_DOM: boolean;

Types

/**
 * Interface for imperative handle factories in DOM components
 */
interface DOMImperativeFactory {
  [key: string]: (...args: JSONValue[]) => void;
}

/**
 * Props interface for DOM components with WebView configuration
 */
interface DOMProps extends Omit<RNWebViewProps, 'source'> {
  /** Whether to resize the native WebView size based on the DOM content size */
  matchContents?: boolean;
  /** Whether to use @expo/dom-webview as the underlying WebView implementation */
  useExpoDOMWebView?: boolean;
}

/**
 * Supported JSON value types for DOM component communication
 */
type JSONValue = boolean | number | string | null | JSONArray | JSONObject;

interface JSONArray extends Array<JSONValue> {}

interface JSONObject {
  [key: string]: JSONValue | undefined;
}

/**
 * Message structure for communication between React Native and DOM components
 */
type BridgeMessage<TData extends JSONValue> = {
  type: string;
  data: TData;
};

Usage Examples

Basic DOM Component

import React from 'react';
import { registerDOMComponent, useDOMImperativeHandle } from 'expo/dom';

interface CanvasComponentProps {
  width: number;
  height: number;
  backgroundColor?: string;
}

interface CanvasHandle {
  drawCircle: (x: number, y: number, radius: number, color: string) => void;
  clear: () => void;
  getImageData: () => string;
}

function CanvasComponent({ width, height, backgroundColor = 'white' }: CanvasComponentProps) {
  const canvasRef = React.useRef<HTMLCanvasElement>(null);
  const handleRef = React.useRef<CanvasHandle>(null);

  // Create imperative handle for React Native to call
  useDOMImperativeHandle<CanvasHandle>(
    handleRef,
    () => ({
      drawCircle: (x: number, y: number, radius: number, color: string) => {
        const canvas = canvasRef.current;
        if (!canvas) return;
        
        const ctx = canvas.getContext('2d');
        if (!ctx) return;
        
        ctx.fillStyle = color;
        ctx.beginPath();
        ctx.arc(x, y, radius, 0, Math.PI * 2);
        ctx.fill();
      },
      clear: () => {
        const canvas = canvasRef.current;
        if (!canvas) return;
        
        const ctx = canvas.getContext('2d');
        if (!ctx) return;
        
        ctx.fillStyle = backgroundColor;
        ctx.fillRect(0, 0, width, height);
      },
      getImageData: () => {
        const canvas = canvasRef.current;
        if (!canvas) return '';
        
        return canvas.toDataURL();
      }
    }),
    [width, height, backgroundColor]
  );

  return (
    <canvas
      ref={canvasRef}
      width={width}
      height={height}
      style={{ backgroundColor, display: 'block' }}
    />
  );
}

// Register the component as a DOM component
registerDOMComponent(CanvasComponent);

Environment Detection

import { IS_DOM } from 'expo/dom';

function MyComponent() {
  if (IS_DOM) {
    // Code running in DOM component context
    // Has access to DOM APIs, document, window, etc.
    return (
      <div>
        <h1>DOM Component</h1>
        <p>This is running in a WebView with full DOM access</p>
      </div>
    );
  } else {
    // Code running in React Native context
    return (
      <View>
        <Text>React Native Component</Text>
      </View>
    );
  }
}

Communication Patterns

Message-Based Communication

DOM components communicate with React Native through a message-passing system:

// In DOM component
function sendMessageToReactNative(type: string, data: any) {
  // Messages are sent through the WebView bridge
  if (typeof window.ReactNativeWebView !== 'undefined') {
    window.ReactNativeWebView.postMessage(JSON.stringify({ type, data }));
  }
}

// Example: Notify React Native of user interaction
function handleUserClick(userData: any) {
  sendMessageToReactNative('user_interaction', {
    action: 'click',
    data: userData,
    timestamp: Date.now(),
  });
}

Props and State Management

// DOM components receive props from React Native
function MyDOMComponent({ initialData, onDataChange }) {
  const [data, setData] = useState(initialData);

  const handleChange = (newData) => {
    setData(newData);
    onDataChange?.(newData); // Callback to React Native
  };

  return (
    <div>
      {/* Component UI */}
    </div>
  );
}

Platform Considerations

WebView Context

DOM components run within WebView contexts, which means:

  • Full access to DOM APIs and web standards
  • Isolated JavaScript context from React Native
  • Communication happens through bridge messages
  • Performance characteristics of web rendering

Security Considerations

// DOM components should validate all data from React Native
function validateProps(props: any): boolean {
  // Implement proper validation
  return typeof props === 'object' && props !== null;
}

function SecureDOMComponent(props: any) {
  if (!validateProps(props)) {
    return <div>Invalid props provided</div>;
  }

  return (
    <div>
      {/* Safe to use props */}
    </div>
  );
}