---
id: sm-use-dom
name: "use-dom"
url: https://skills.yangsir.net/skill/sm-use-dom
author: expo
domain: ai-frontend-engineering
tags: ["dom-manipulation", "javascript", "web-apis", "frontend-development", "browser-apis"]
install_count: 21500
rating: 4.60 (358 reviews)
github: https://github.com/expo/skills
---

# use-dom

> 提供与DOM操作相关的AI代理技能集合，辅助前端开发者在Expo项目中高效处理界面元素。

**Stats**: 21,500 installs · 4.6/5 (358 reviews)

## Before / After 对比

### DOM操作优化，前端交互更流畅

## Readme

# 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 (
    <div style={{ padding: 20 }}>
      <h2>Chart Data</h2>
      <ul>
        {data.map((value, i) => (
          <li key={i}>{value}</li>
        ))}
      </ul>
    </div>
  );
}

```

## 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 <div>{content}</div>;
}

```

### Common `dom` Prop Options

```
// Disable body scrolling
<DOMComponent dom={{ scrollEnabled: false }} />

// Flow under the notch (disable safe area insets)
<DOMComponent dom={{ contentInsetAdjustmentBehavior: "never" }} />

// Control size manually
<DOMComponent dom={{ style: { width: 300, height: 400 } }} />

// Combine options
<DOMComponent
  dom={{
    scrollEnabled: false,
    contentInsetAdjustmentBehavior: "never",
    style: { width: '100%', height: 500 }
  }}
/>

```

## 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 (
    <DOMComponent
      showAlert={async (message: string) => {
        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<void>;
  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 <button onClick={handleClick}>Trigger Native Action</button>;
}

```

## 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 (
    <SyntaxHighlighter language={language} style={docco}>
      {code}
    </SyntaxHighlighter>
  );
}

```

```
// 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 (
    <LineChart width={400} height={300} data={data}>
      <CartesianGrid strokeDasharray="3 3" />
      <XAxis dataKey="name" />
      <YAxis />
      <Tooltip />
      <Line type="monotone" dataKey="value" stroke="#8884d8" />
    </LineChart>
  );
}

```

## 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 (
    <div className="container">
      <h1 className="title">Styled Content</h1>
    </div>
  );
}

```

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 (
    <div style={styles.container}>
      <h1 style={styles.title}>Styled Content</h1>
    </div>
  );
}

```

## Expo Router in DOM Components

The expo-router `<Link />` 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 (
    <nav>
      <Link href="/about">About</Link>
      <button onClick={() => router.push("/settings")}>Settings</button>
    </nav>
  );
}

```

### 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 <DOMComponent id={id as string} pathname={pathname} />;
}

```

```
// 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 (
    <div>
      <p>Current ID: {id}</p>
      <p>Current Path: {pathname}</p>
    </div>
  );
}

```

## 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 <div>{IS_DOM ? "Running in DOM component" : "Running natively"}</div>;
}

```

## 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 <img src={logo} alt="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 (
    <View style={{ flex: 1 }}>
      <Text>Native content above</Text>

      <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

Weekly Installs8.1KRepository[expo/skills](https://github.com/expo/skills)GitHub Stars1.5KFirst SeenJan 19, 2026Security Audits[Gen Agent Trust HubPass](/expo/skills/use-dom/security/agent-trust-hub)[SocketPass](/expo/skills/use-dom/security/socket)[SnykPass](/expo/skills/use-dom/security/snyk)Installed onopencode6.0Kgithub-copilot6.0Kcodex5.9Kgemini-cli5.8Kcursor5.5Kamp4.8K

---
*Source: https://skills.yangsir.net/skill/sm-use-dom*
*Markdown mirror: https://skills.yangsir.net/api/skill/sm-use-dom/markdown*