À propos
expo-dom permet à du code React web de s'exécuter dans des webviews natives tout en fonctionnant normalement sur le web, permettant une migration progressive des composants web vers des applications natives. C'est idéal pour utiliser des bibliothèques exclusivement web comme des graphiques ou des surligneurs de syntaxe qui dépendent des API DOM. Cette approche vous permet d'intégrer des composants web existants dans des applications natives sans avoir à les réécrire.
Installation rapide
Claude Code
Recommandénpx skills add expo/skills -a claude-code/plugin add https://github.com/expo/skillsgit clone https://github.com/expo/skills.git ~/.claude/skills/expo-domCopiez et collez cette commande dans Claude Code pour installer cette compétence
Documentation
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 —
_layoutfiles 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
Dépôt GitHub
Frequently asked questions
What is the expo-dom skill?
expo-dom is a Claude Skill by expo. Skills package instructions and resources that Claude loads on demand, so Claude can perform expo-dom-related tasks without extra prompting.
How do I install expo-dom?
Use the install commands on this page: add expo-dom to Claude Code as a plugin, or clone its repository into your skills directory, then restart Claude so it picks up the skill.
What category does expo-dom belong to?
expo-dom is in the Meta category, tagged general.
Is expo-dom free to use?
Yes. expo-dom is listed on AIMCP and free to install. It runs inside Claude, so no separate service account is required to use the skill itself.
Compétences associées
Cette compétence propose une configuration éprouvée en production pour Content Collections, un outil axé sur TypeScript qui transforme des fichiers Markdown/MDX en collections de données typées de manière sûre avec une validation Zod. Utilisez-la lors de la création de blogs, de sites de documentation ou d'applications Vite + React riches en contenu pour garantir la sécurité de typage et la validation automatique du contenu. Elle couvre tout, de la configuration du plugin Vite et de la compilation MDX à l'optimisation des déploiements et la validation des schémas.
Cette compétence permet aux développeurs de créer des applications avec la plateforme de marchés prédictifs Polymarket, incluant l'intégration d'API pour le trading et les données de marché. Elle fournit également une diffusion de données en temps réel via WebSocket pour surveiller les transactions en direct et l'activité du marché. Utilisez-la pour mettre en œuvre des stratégies de trading ou pour créer des outils traitant les mises à jour de marché en direct.
Cette compétence aide les développeurs à créer des plugins OpenCode qui s'interconnectent avec plus de 25 types d'événements tels que les commandes, les fichiers et les opérations LSP. Elle fournit la structure du plugin, les spécifications de l'API événementielle et les modèles d'implémentation pour les modules JavaScript/TypeScript. Utilisez-la lorsque vous avez besoin d'intercepter, de surveiller ou d'étendre le cycle de vie de l'assistant IA OpenCode avec une logique personnalisée pilotée par les événements.
SGLang est un framework de service LLM haute performance spécialisé dans la génération rapide et structurée pour les workflows JSON, regex et agentiques grâce à son cache de préfixe RadixAttention. Il offre une inférence nettement plus rapide, particulièrement pour les tâches avec des préfixes répétés, ce qui le rend idéal pour les sorties complexes et structurées ainsi que les conversations multi-tours. Choisissez SGLang plutôt que des alternatives comme vLLM lorsque vous avez besoin d'un décodage contraint ou que vous construisez des applications avec un partage étendu de préfixes.
