use-dom

Par expo · skills

Utilisez les composants DOM Expo pour exécuter du code web dans une webview sur mobile natif et tel quel sur le web. Migrez progressivement votre code web vers le natif.

npx skills add https://github.com/expo/skills --skill use-dom

Qu'est-ce que les DOM Components ?

Les DOM components permettent à du code web de s'exécuter verbatim dans une webview sur les plateformes natives tout en s'affichant tel quel sur le web. Cela permet d'utiliser des bibliothèques web uniquement comme recharts, react-syntax-highlighter, ou n'importe quelle bibliothèque React web dans votre app Expo sans modification.

Quand utiliser les DOM Components

Utilisez les DOM components quand vous avez besoin de :

  • Bibliothèques web uniquement — Graphiques (recharts, chart.js), surligneurs de syntaxe, éditeurs de texte enrichi, ou n'importe quelle bibliothèque dépendant des APIs DOM
  • Migrer du code web — Apporter des composants React web existants vers native sans réécriture
  • Mises en page HTML/CSS complexes — Quand les fonctionnalités CSS ne sont pas disponibles dans React Native
  • iframes ou embeds — Intégrer du contenu externe qui nécessite un contexte navigateur
  • Canvas ou WebGL — APIs graphiques web non disponibles nativement

Quand NE PAS utiliser les DOM Components

Évitez les DOM components quand :

  • Les performances natives sont critiques — Les webviews ajoutent de la surcharge
  • Interface simple — Les composants React Native sont plus efficaces pour les mises en page basiques
  • Intégration native profonde — Utilisez les modules locaux à la place pour les APIs natives
  • Routes de mise en page — Les fichiers _layout ne peuvent pas être des DOM components

DOM Component de base

Créez un nouveau fichier avec la directive 'use dom'; en haut :

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

Règles pour les DOM Components

  1. Doit avoir la directive 'use dom'; en haut du fichier
  2. Export par défaut unique — Un composant React par fichier
  3. Son propre fichier — Ne peut pas être défini inline ou combiné avec des composants natifs
  4. Props sérialisables uniquement — Chaînes, nombres, booléens, tableaux, objets bruts
  5. Inclure le CSS dans le fichier composant — Les DOM components s'exécutent dans un contexte isolé

La prop dom

Chaque DOM component reçoit une prop spéciale dom pour la configuration de la webview. Tapez-la toujours dans vos props :

"use dom";

interface Props {
  content: string;
  dom: import("expo/dom").DOMProps;
}

export default function MyComponent({ content }: Props) {
  return <div>{content}</div>;
}

Options courantes de la prop dom

// Désactiver le défilement du body
<DOMComponent dom={{ scrollEnabled: false }} />

// Afficher sous l'enoche (désactiver les insets de zone sûre)
<DOMComponent dom={{ contentInsetAdjustmentBehavior: "never" }} />

// Contrôler la taille manuellement
<DOMComponent dom={{ style: { width: 300, height: 400 } }} />

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

Exposer les actions natives à la webview

Passez des fonctions async en tant que props pour exposer les fonctionnalités natives au 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 }) => {
        // Enregistrer dans le stockage natif, la base de données, 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>;
}

Utiliser des bibliothèques web

Les DOM components peuvent utiliser n'importe quelle bibliothèque web :

// 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 dans les DOM Components

Les imports CSS doivent être dans le fichier du DOM component puisqu'ils s'exécutent dans un contexte isolé :

// components/styled-component.tsx
"use dom";

import "@/styles.css"; // Fichier CSS dans le même répertoire

export default function StyledComponent({
  dom,
}: {
  dom: import("expo/dom").DOMProps;
}) {
  return (
    <div className="container">
      <h1 className="title">Styled Content</h1>
    </div>
  );
}

Ou utiliser des styles inline / 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 dans les DOM Components

Le composant <Link /> de expo-router et l'API router fonctionnent à l'intérieur des 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>
  );
}

APIs Router qui nécessitent des props

Ces hooks ne fonctionnent pas directement dans les DOM components car ils ont besoin d'un accès synchrone à l'état de routage natif :

  • useLocalSearchParams()
  • useGlobalSearchParams()
  • usePathname()
  • useSegments()
  • useRootNavigation()
  • useRootNavigationState()

Solution : Lisez ces valeurs dans le parent natif et passez-les en tant que 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>
  );
}

Détecter l'environnement DOM

Vérifiez si le code s'exécute dans un 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>;
}

Ressources

Préférez importer les ressources plutôt que d'utiliser le répertoire public :

"use dom";

// Bon - regroupé avec le composant
const logo = require("../assets/logo.png");

export default function Component({
  dom,
}: {
  dom: import("expo/dom").DOMProps;
}) {
  return <img src={logo} alt="Logo" />;
}

Utilisation depuis les composants natifs

Importez et utilisez les DOM components comme des composants ordinaires :

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

Comportement par plateforme

Plateforme Comportement
iOS Affiché dans WKWebView
Android Affiché dans WebView
Web Affiché tel quel (sans wrapper webview)

Sur le web, la prop dom est ignorée puisqu'aucune webview n'est nécessaire.

Conseils

  • Les DOM components se rechargent à chaud lors du développement
  • Gardez les DOM components ciblés — ne mettez pas des écrans entiers dans les webviews
  • Utilisez les composants natifs pour la navigation, les DOM components pour le contenu spécialisé
  • Testez sur toutes les plateformes — le rendu web peut différer légèrement des webviews natives
  • Les grands DOM components peuvent impacter les performances — profilez si nécessaire
  • La webview a son propre contexte JavaScript — ne peut pas partager directement l'état avec le natif

Skills similaires