use-dom

安装量: 8.2K
排名: #301

安装

npx skills add https://github.com/expo/skills --skill use-dom

What are DOM Components?

DOM components allow web code to run verbatim in a webview on native platforms while rendering as-is on web. This enables using web-only libraries like recharts, react-syntax-highlighter, or any React web library in your Expo app without modification.

When to Use DOM Components

Use DOM components when you need:

Web-only libraries — Charts (recharts, chart.js), syntax highlighters, rich text editors, or any library that depends on DOM APIs Migrating web code — Bring existing React web components to native without rewriting Complex HTML/CSS layouts — When CSS features aren't available in React Native iframes or embeds — Embedding external content that requires a browser context Canvas or WebGL — Web graphics APIs not available natively When NOT to Use DOM Components

Avoid DOM components when:

Native performance is critical — Webviews add overhead Simple UI — React Native components are more efficient for basic layouts Deep native integration — Use local modules instead for native APIs Layout routes — _layout files cannot be DOM components Basic DOM Component

Create a new file with the 'use dom'; directive at the top:

// components/WebChart.tsx "use dom";

export default function WebChart({ data, }: { data: number[]; dom: import("expo/dom").DOMProps; }) { return (

Chart Data

    {data.map((value, i) => (
  • {value}
  • ))}
); }

Rules for DOM Components Must have 'use dom'; directive at the top of the file Single default export — One React component per file Own file — Cannot be defined inline or combined with native components Serializable props only — Strings, numbers, booleans, arrays, plain objects Include CSS in the component file — DOM components run in isolated context The dom Prop

Every DOM component receives a special dom prop for webview configuration. Always type it in your props:

"use dom";

interface Props { content: string; dom: import("expo/dom").DOMProps; }

export default function MyComponent({ content }: Props) { return

{content}
; }

Common dom Prop Options // Disable body scrolling

// Flow under the notch (disable safe area insets)

// Control size manually

// Combine options

Exposing Native Actions to the Webview

Pass async functions as props to expose native functionality to the DOM component:

// app/index.tsx (native) import { Alert } from "react-native"; import DOMComponent from "@/components/dom-component";

export default function Screen() { return ( { Alert.alert("From Web", message); }} saveData={async (data: { name: string; value: number }) => { // Save to native storage, database, etc. console.log("Saving:", data); return { success: true }; }} /> ); }

// components/dom-component.tsx "use dom";

interface Props { showAlert: (message: string) => Promise; saveData: (data: { name: string; value: number; }) => Promise<{ success: boolean }>; dom?: import("expo/dom").DOMProps; }

export default function DOMComponent({ showAlert, saveData }: Props) { const handleClick = async () => { await showAlert("Hello from the webview!"); const result = await saveData({ name: "test", value: 42 }); console.log("Save result:", result); };

return ; }

Using Web Libraries

DOM components can use any web library:

// components/syntax-highlight.tsx "use dom";

import SyntaxHighlighter from "react-syntax-highlighter"; import { docco } from "react-syntax-highlighter/dist/esm/styles/hljs";

interface Props { code: string; language: string; dom?: import("expo/dom").DOMProps; }

export default function SyntaxHighlight({ code, language }: Props) { return ( {code} ); }

// components/chart.tsx "use dom";

import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, } from "recharts";

interface Props { data: Array<{ name: string; value: number }>; dom: import("expo/dom").DOMProps; }

export default function Chart({ data }: Props) { return ( ); }

CSS in DOM Components

CSS imports must be in the DOM component file since they run in isolated context:

// components/styled-component.tsx "use dom";

import "@/styles.css"; // CSS file in same directory

export default function StyledComponent({ dom, }: { dom: import("expo/dom").DOMProps; }) { return (

Styled Content

); }

Or use inline styles / CSS-in-JS:

"use dom";

const styles = { container: { padding: 20, backgroundColor: "#f0f0f0", }, title: { fontSize: 24, color: "#333", }, };

export default function StyledComponent({ dom, }: { dom: import("expo/dom").DOMProps; }) { return (

Styled Content

); }

Expo Router in DOM Components

The expo-router component and router API work inside DOM components:

"use dom";

import { Link, useRouter } from "expo-router";

export default function Navigation({ dom, }: { dom: import("expo/dom").DOMProps; }) { const router = useRouter();

return (

); }

Router APIs That Require Props

These hooks don't work directly in DOM components because they need synchronous access to native routing state:

useLocalSearchParams() useGlobalSearchParams() usePathname() useSegments() useRootNavigation() useRootNavigationState()

Solution: Read these values in the native parent and pass as props:

// app/[id].tsx (native) import { useLocalSearchParams, usePathname } from "expo-router"; import DOMComponent from "@/components/dom-component";

export default function Screen() { const { id } = useLocalSearchParams(); const pathname = usePathname();

return ; }

// components/dom-component.tsx "use dom";

interface Props { id: string; pathname: string; dom?: import("expo/dom").DOMProps; }

export default function DOMComponent({ id, pathname }: Props) { return (

Current ID: {id}

Current Path: {pathname}

); }

Detecting DOM Environment

Check if code is running in a DOM component:

"use dom";

import { IS_DOM } from "expo/dom";

export default function Component({ dom, }: { dom?: import("expo/dom").DOMProps; }) { return

{IS_DOM ? "Running in DOM component" : "Running natively"}
; }

Assets

Prefer requiring assets instead of using the public directory:

"use dom";

// Good - bundled with the component const logo = require("../assets/logo.png");

export default function Component({ dom, }: { dom: import("expo/dom").DOMProps; }) { return Logo; }

Usage from Native Components

Import and use DOM components like regular components:

// app/index.tsx import { View, Text } from "react-native"; import WebChart from "@/components/web-chart"; import CodeBlock from "@/components/code-block";

export default function HomeScreen() { return ( Native content above

  <WebChart data={[10, 20, 30, 40, 50]} dom={{ style: { height: 300 } }} />

  <CodeBlock
    code="const x = 1;"
    language="javascript"
    dom={{ scrollEnabled: true }}
  />

  <Text>Native content below</Text>
</View>

); }

Platform Behavior Platform Behavior iOS Rendered in WKWebView Android Rendered in WebView Web Rendered as-is (no webview wrapper)

On web, the dom prop is ignored since no webview is needed.

Tips DOM components hot reload during development Keep DOM components focused — don't put entire screens in webviews Use native components for navigation chrome, DOM components for specialized content Test on all platforms — web rendering may differ slightly from native webviews Large DOM components may impact performance — profile if needed The webview has its own JavaScript context — cannot directly share state with native

返回排行榜