🎯

use-dom

🎯Skill

from marcoodignoti/couple-diary

VibeIndex|
What it does

Enables running web-only React libraries and components in Expo apps by rendering them in a webview on native platforms and directly on web.

πŸ“¦

Part of

marcoodignoti/couple-diary(9 items)

use-dom

Installation

πŸ“‹ No install commands found in docs. Showing default command. Check GitHub for actual instructions.
Quick InstallInstall with npx
npx skills add marcoodignoti/couple-diary --skill use-dom
2Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

Use Expo DOM components to run web code in a webview on native and as-is on web. Migrate web code to native incrementally.

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:

```tsx

// 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

  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:

```tsx

"use dom";

interface Props {

content: string;

dom: import("expo/dom").DOMProps;

}

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

return

{content}
;

}

```

Common `dom` Prop Options

```tsx

// Disable body scrolling

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

// Control size manually

// Combine options

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:

```tsx

// app/index.tsx (native)

import { Alert } from "react-native";

import DOMComponent from "@/components/dom-component";

export default function Screen() {

return (

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 };

}}

/>

);

}

```

```tsx

// 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:

```tsx

// 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}

);

}

```

```tsx

// 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:

```tsx

// 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:

```tsx

"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:

```tsx

"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:

```tsx

// 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 ;

}

```

```tsx

// 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:

```tsx

"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:

```tsx

"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:

```tsx

// 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

code="const x = 1;"

language="javascript"

dom={{ scrollEnabled: true }}

/>

Native content below

);

}

```

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