← Expo2CONTENT HISTORY
WHAT CHANGED
Update to Expo2
Snapshot Nov 6, 2026 · 18:22 UTC · version 1.14.6
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Save Expo2 and check future changes in Following.
Up to 3 plugins free · no card required.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "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.",
"included_files": [
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 371
}
],
"name": "expo-dom",
"skill_md_contents": "---\nname: expo-dom\ndescription: 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.\nlicense: MIT\n---\n\n## What are DOM Components?\n\nDOM 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.\n\n## When to Use DOM Components\n\nUse DOM components when you need:\n\n- **Web-only libraries** — Charts (recharts, chart.js), syntax highlighters, rich text editors, or any library that depends on DOM APIs\n- **Migrating web code** — Bring existing React web components to native without rewriting\n- **Complex HTML/CSS layouts** — When CSS features aren't available in React Native\n- **iframes or embeds** — Embedding external content that requires a browser context\n- **Canvas or WebGL** — Web graphics APIs not available natively\n\n## When NOT to Use DOM Components\n\nAvoid DOM components when:\n\n- **Native performance is critical** — Webviews add overhead\n- **Simple UI** — React Native components are more efficient for basic layouts\n- **Deep native integration** — Use local modules instead for native APIs\n- **Layout routes** — `_layout` files cannot be DOM components\n\n## Basic DOM Component\n\nCreate a new file with the `'use dom';` directive at the top:\n\n```tsx\n// components/WebChart.tsx\n\"use dom\";\n\nexport default function WebChart({\n data,\n}: {\n data: number[];\n dom: import(\"expo/dom\").DOMProps;\n}) {\n return (\n <div style={{ padding: 20 }}>\n <h2>Chart Data</h2>\n <ul>\n {data.map((value, i) => (\n <li key={i}>{value}</li>\n ))}\n </ul>\n </div>\n );\n}\n```\n\n## Rules for DOM Components\n\n1. **Must have `'use dom';` directive** at the top of the file\n2. **Single default export** — One React component per file\n3. **Own file** — Cannot be defined inline or combined with native components\n4. **Serializable props only** — Strings, numbers, booleans, arrays, plain objects\n5. **Include CSS in the component file** — DOM components run in isolated context\n\n## The `dom` Prop\n\nEvery DOM component receives a special `dom` prop for webview configuration. Always type it in your props:\n\n```tsx\n\"use dom\";\n\ninterface Props {\n content: string;\n dom: import(\"expo/dom\").DOMProps;\n}\n\nexport default function MyComponent({ content }: Props) {\n return <div>{content}</div>;\n}\n```\n\n### Common `dom` Prop Options\n\n```tsx\n// Disable body scrolling\n<DOMComponent dom={{ scrollEnabled: false }} />\n\n// Flow under the notch (disable safe area insets)\n<DOMComponent dom={{ contentInsetAdjustmentBehavior: \"never\" }} />\n\n// Control size manually\n<DOMComponent dom={{ style: { width: 300, height: 400 } }} />\n\n// Combine options\n<DOMComponent\n dom={{\n scrollEnabled: false,\n contentInsetAdjustmentBehavior: \"never\",\n style: { width: '100%', height: 500 }\n }}\n/>\n```\n\n## Exposing Native Actions to the Webview\n\nPass async functions as props to expose native functionality to the DOM component:\n\n```tsx\n// app/index.tsx (native)\nimport { Alert } from \"react-native\";\nimport DOMComponent from \"@/components/dom-component\";\n\nexport default function Screen() {\n return (\n <DOMComponent\n showAlert={async (message: string) => {\n Alert.alert(\"From Web\", message);\n }}\n saveData={async (data: { name: string; value: number }) => {\n // Save to native storage, database, etc.\n console.log(\"Saving:\", data);\n return { success: true };\n }}\n />\n );\n}\n```\n\n```tsx\n// components/dom-component.tsx\n\"use dom\";\n\ninterface Props {\n showAlert: (message: string) => Promise<void>;\n saveData: (data: {\n name: string;\n value: number;\n }) => Promise<{ success: boolean }>;\n dom?: import(\"expo/dom\").DOMProps;\n}\n\nexport default function DOMComponent({ showAlert, saveData }: Props) {\n const handleClick = async () => {\n await showAlert(\"Hello from the webview!\");\n const result = await saveData({ name: \"test\", value: 42 });\n console.log(\"Save result:\", result);\n };\n\n return <button onClick={handleClick}>Trigger Native Action</button>;\n}\n```\n\n## Using Web Libraries\n\nDOM components can use any web library:\n\n```tsx\n// components/syntax-highlight.tsx\n\"use dom\";\n\nimport SyntaxHighlighter from \"react-syntax-highlighter\";\nimport { docco } from \"react-syntax-highlighter/dist/esm/styles/hljs\";\n\ninterface Props {\n code: string;\n language: string;\n dom?: import(\"expo/dom\").DOMProps;\n}\n\nexport default function SyntaxHighlight({ code, language }: Props) {\n return (\n <SyntaxHighlighter language={language} style={docco}>\n {code}\n </SyntaxHighlighter>\n );\n}\n```\n\n```tsx\n// components/chart.tsx\n\"use dom\";\n\nimport {\n LineChart,\n Line,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n} from \"recharts\";\n\ninterface Props {\n data: Array<{ name: string; value: number }>;\n dom: import(\"expo/dom\").DOMProps;\n}\n\nexport default function Chart({ data }: Props) {\n return (\n <LineChart width={400} height={300} data={data}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"name\" />\n <YAxis />\n <Tooltip />\n <Line type=\"monotone\" dataKey=\"value\" stroke=\"#8884d8\" />\n </LineChart>\n );\n}\n```\n\n## CSS in DOM Components\n\nCSS imports must be in the DOM component file since they run in isolated context:\n\n```tsx\n// components/styled-component.tsx\n\"use dom\";\n\nimport \"@/styles.css\"; // CSS file in same directory\n\nexport default function StyledComponent({\n dom,\n}: {\n dom: import(\"expo/dom\").DOMProps;\n}) {\n return (\n <div className=\"container\">\n <h1 className=\"title\">Styled Content</h1>\n </div>\n );\n}\n```\n\nOr use inline styles / CSS-in-JS:\n\n```tsx\n\"use dom\";\n\nconst styles = {\n container: {\n padding: 20,\n backgroundColor: \"#f0f0f0\",\n },\n title: {\n fontSize: 24,\n color: \"#333\",\n },\n};\n\nexport default function StyledComponent({\n dom,\n}: {\n dom: import(\"expo/dom\").DOMProps;\n}) {\n return (\n <div style={styles.container}>\n <h1 style={styles.title}>Styled Content</h1>\n </div>\n );\n}\n```\n\n## Expo Router in DOM Components\n\nThe expo-router `<Link />` component and router API work inside DOM components:\n\n```tsx\n\"use dom\";\n\nimport { Link, useRouter } from \"expo-router\";\n\nexport default function Navigation({\n dom,\n}: {\n dom: import(\"expo/dom\").DOMProps;\n}) {\n const router = useRouter();\n\n return (\n <nav>\n <Link href=\"/about\">About</Link>\n <button onClick={() => router.push(\"/settings\")}>Settings</button>\n </nav>\n );\n}\n```\n\n### Router APIs That Require Props\n\nThese hooks don't work directly in DOM components because they need synchronous access to native routing state:\n\n- `useLocalSearchParams()`\n- `useGlobalSearchParams()`\n- `usePathname()`\n- `useSegments()`\n- `useRootNavigation()`\n- `useRootNavigationState()`\n\n**Solution:** Read these values in the native parent and pass as props:\n\n```tsx\n// app/[id].tsx (native)\nimport { useLocalSearchParams, usePathname } from \"expo-router\";\nimport DOMComponent from \"@/components/dom-component\";\n\nexport default function Screen() {\n const { id } = useLocalSearchParams();\n const pathname = usePathname();\n\n return <DOMComponent id={id as string} pathname={pathname} />;\n}\n```\n\n```tsx\n// components/dom-component.tsx\n\"use dom\";\n\ninterface Props {\n id: string;\n pathname: string;\n dom?: import(\"expo/dom\").DOMProps;\n}\n\nexport default function DOMComponent({ id, pathname }: Props) {\n return (\n <div>\n <p>Current ID: {id}</p>\n <p>Current Path: {pathname}</p>\n </div>\n );\n}\n```\n\n## Detecting DOM Environment\n\nCheck if code is running in a DOM component:\n\n```tsx\n\"use dom\";\n\nimport { IS_DOM } from \"expo/dom\";\n\nexport default function Component({\n dom,\n}: {\n dom?: import(\"expo/dom\").DOMProps;\n}) {\n return <div>{IS_DOM ? \"Running in DOM component\" : \"Running natively\"}</div>;\n}\n```\n\n## Assets\n\nPrefer requiring assets instead of using the public directory:\n\n```tsx\n\"use dom\";\n\n// Good - bundled with the component\nconst logo = require(\"../assets/logo.png\");\n\nexport default function Component({\n dom,\n}: {\n dom: import(\"expo/dom\").DOMProps;\n}) {\n return <img src={logo} alt=\"Logo\" />;\n}\n```\n\n## Usage from Native Components\n\nImport and use DOM components like regular components:\n\n```tsx\n// app/index.tsx\nimport { View, Text } from \"react-native\";\nimport WebChart from \"@/components/web-chart\";\nimport CodeBlock from \"@/components/code-block\";\n\nexport default function HomeScreen() {\n return (\n <View style={{ flex: 1 }}>\n <Text>Native content above</Text>\n\n <WebChart data={[10, 20, 30, 40, 50]} dom={{ style: { height: 300 } }} />\n\n <CodeBlock\n code=\"const x = 1;\"\n language=\"javascript\"\n dom={{ scrollEnabled: true }}\n />\n\n <Text>Native content below</Text>\n </View>\n );\n}\n```\n\n## Platform Behavior\n\n| Platform | Behavior |\n| -------- | ----------------------------------- |\n| iOS | Rendered in WKWebView |\n| Android | Rendered in WebView |\n| Web | Rendered as-is (no webview wrapper) |\n\nOn web, the `dom` prop is ignored since no webview is needed.\n\n## Tips\n\n- DOM components hot reload during development\n- Keep DOM components focused — don't put entire screens in webviews\n- Use native components for navigation chrome, DOM components for specialized content\n- Test on all platforms — web rendering may differ slightly from native webviews\n- Large DOM components may impact performance — profile if needed\n- The webview has its own JavaScript context — cannot directly share state with native\n\n## Submitting Feedback\nWhen the user requests feedback submission, use:\n```bash\nnpx --yes @expo/agent-cli@1.0.23 feedback --category skills --subject \"expo-dom\" \"<actionable feedback>\"\n```\nInclude only relevant technical details; keep credentials, private data, and conversation contents out of feedback.\n"
}SHA-256 of public snapshot: 52e4db87266a3993011b13e43b44e34aaf5c19f1e00c818d4166e9ff7fb9f221