Source profileQuality 93/100Review permissions

expo/skills/plugins/expo/skills/expo-dom/SKILL.md

expo-dom

Framework (OSS). Use Expo DOM components to run web code in a webview on native and as-is on web. Migrate web code to native incrementally. For the end-to-end migration of a whole web app, use the expo-web-to-native skill.

Source repository stars
2,458
Declared platforms
0
Static risk flags
2
Last source update
2026-08-24
Source checked
2026-08-25

Decision brief

What it does: where it fits

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.

Best for

  • 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

Not for

  • Native performance is critical — Webviews add overhead
  • Simple UI — React Native components are more efficient for basic layouts

Compatibility matrix

Platform support, with evidence labels

PlatformStatusEvidenceWhat to check
CodexNot declaredNo explicit evidencePortability before use
Claude CodeNot declaredNo explicit evidencePortability before use
CursorNot declaredNo explicit evidencePortability before use
Gemini CLINot declaredNo explicit evidencePortability before use
Open the compatibility checker

Installation

Inspect first. Install second.

The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

Source-detected install commandSource
npx skills add https://github.com/expo/skills --skill "plugins/expo/skills/expo-dom"
Safe inspection promptEditorial

Inspect the Agent Skill "expo-dom" from https://github.com/expo/skills/blob/472d040092900dc8bbf84dc7efb0c90abff77a0d/plugins/expo/skills/expo-dom/SKILL.md at commit 472d040092900dc8bbf84dc7efb0c90abff77a0d. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

Workflow

What the source asks the agent to do

  1. 01

    Usage from Native Components

    Import and use DOM components like regular components:

    Import and use DOM components like regular components:
  2. 02

    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.

    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 you…
  3. 03

    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 APIsMigrating web code — Bring existing React web components to native without rewritingComplex HTML/CSS layouts — When CSS features aren't available in React Native
  4. 04

    When NOT to Use DOM Components

    Avoid DOM components when:

    Native performance is critical — Webviews add overheadSimple UI — React Native components are more efficient for basic layoutsDeep native integration — Use local modules instead for native APIs
  5. 05

    Basic DOM Component

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

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

Permission review

Static risk signals and limitations

Writes files

medium · line 27

The documentation asks the agent to create, modify, or delete local files.

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

Runs scripts

medium · line 416

The documentation asks the agent to run terminal commands or scripts.

npx --yes submit-expo-feedback@latest --category skills --subject "expo-dom" "<actionable feedback>"

Evidence record

Why each signal appears

EvidenceSourceComputedTestedEditorial
SignalValueEvidence typeMeaning
Quality score93/100ComputedDocumentation, specificity, maintenance, and trust rules
Repository stars2,458SourceRepository attention, not individual Skill quality
Compatibility0 platformsSourceDeclared in the catalog source record
Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

Pinned source

Provenance and original SKILL.md

Repository
expo/skills
Skill path
plugins/expo/skills/expo-dom/SKILL.md
Commit
472d040092900dc8bbf84dc7efb0c90abff77a0d
License
MIT
Collected
2026-08-25
Default branch
main
View the original SKILL.md

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

  1. Must have 'use dom'; directive at the top of the file
  2. Single default export — One React component per file
  3. Own file — Cannot be defined inline or combined with native components
  4. Serializable props only — Strings, numbers, booleans, arrays, plain objects
  5. 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

PlatformBehavior
iOSRendered in WKWebView
AndroidRendered in WebView
WebRendered 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

Submitting Feedback

If you encounter errors, misleading or outdated information in this skill, report it so Expo can improve:

npx --yes submit-expo-feedback@latest --category skills --subject "expo-dom" "<actionable feedback>"

Only submit when you have something specific and actionable to report. Include as much relevant context as possible. If an AI agent repeatedly failed or the user had to take over an Expo task, load the expo-skill-feedback skill and follow its eval-candidate flow instead of reusing the command above.

Frequently asked questions

What to verify before installation and use

What does the expo-dom source document cover?

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.

How do I install expo-dom?

The source record exposes this install command: npx skills add https://github.com/expo/skills --skill "plugins/expo/skills/expo-dom". Inspect the command and pinned source before running it.

Which permission-related actions were detected?

Static rules flagged write-files, exec-script in the source; the page lists the matching lines and excerpts.

Alternatives

Compare before choosing

Computed 10045,511

coreyhaines31/marketingskills

ab-testing

When the user wants to plan, design, or implement an A/B test or experiment, or build a growth experimentation program. Also use when the user mentions "A/B test," "split test," "experiment," "test this change," "variant copy," "multivariate test," "hypothesis," "should I test this," "which version is better," "test two versions," "statistical significance," "how long should I run this test," "growth experiments," "experiment velocity," "experiment backlog," "ICE score," "experimentation program

Computed 10029,034

garrytan/gbrain

bulk-ingestion

End-to-end discipline for turning any large data source (audio libraries, email takeouts, document corpora, chat exports, API dumps) into brain pages at scale. The lifecycle spine: SCHEMA → ACCESS → TRIAL → EVALUATE → IMPROVE → CODIFY → TEST → SKILLIFY → BULK → MONITOR. State is tracked in a durable JSON manifest (see MANIFEST-PATTERN.md) so any crash, session boundary, or subagent fan-out resumes from ground truth instead of memory.

Computed 10024,921

alirezarezvani/claude-skills

app-store-optimization

App Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklist

Computed 1005,241

dotnet/skills

migrate-vstest-to-mtp

Migrates .NET test projects from VSTest to Microsoft.Testing.Platform (MTP). Use when user asks to "migrate to MTP", "switch from VSTest", "enable Microsoft.Testing.Platform", "use MTP runner", set OutputType=Exe only for test projects in Directory.Build.props, or mentions EnableMSTestRunner, EnableNUnitRunner, or UseMicrosoftTestingPlatformRunner. USE FOR: MTP behavioral differences vs VSTest (exit code 8, zero tests discovered, --ignore-exit-code, TESTINGPLATFORM_EXITCODE_IGNORE); centralizing