EXPO SDK 52 expo-router v4 TypeScript

Expo DOM Components'use dom'

Guía de integración para CultivaDash: ejecuta componentes React web (recharts, syntax highlighters, editores) dentro de WebViews en iOS y Android sin reescribir código.

Casos de uso

USAR 'use dom' cuando...

  • Librerías que requieren DOM (recharts, chart.js)
  • Migrar componentes web existentes a nativo
  • CSS avanzado no disponible en React Native
  • Canvas / WebGL / iframes / embeds externos
  • Editores de texto enriquecido (quill, tiptap)

NO USAR 'use dom' cuando...

  • Performance nativa crítica (listas largas, animaciones)
  • UI simple que React Native soporta bien
  • Integración profunda con APIs nativas
  • Archivos _layout de expo-router
  • Componentes con estado compartido síncrono
5 reglas del DOM Component
1
'use dom'; directiva
Primera línea del archivo, obligatoria
2
Default export único
Un componente React por archivo
3
Archivo propio
No inline, no combinado con nativo
4
Props serializables
string, number, boolean, arrays, plain objects
5
CSS aislado
Imports CSS dentro del mismo archivo
Comunicación bidireccional nativo ↔ DOM
Nativo
app/analytics.tsx
iOS · Android · Web
───── Props serializable ──→
campaignData[], userId, dom={}
←── async fn callbacks ──────
onExportCSV(), onAlert()
DOM Component
CampaignChart.tsx
'use dom' · WebView
Componente CultivaDash — CampaignChart.tsx
components/CampaignChart.tsx TSX · DOM
"use dom";                           // ← directiva obligatoria

import { LineChart, Line, XAxis, YAxis, Tooltip, CartesianGrid } from "recharts";

interface CampaignData {
  day: string;
  conversions: number;
  spend: number;
}

interface Props {
  campaignData: CampaignData[];
  onExportCSV: (rows: object[]) => Promise<void>;  // llama Share nativo
  currentUserId: string;
  dom: import("expo/dom").DOMProps;
}

export default function CampaignChart({ campaignData, onExportCSV, currentUserId }: Props) {
  return (
    <div style={{ padding: 20, fontFamily: "system-ui", background: "#0d1117" }}>
      <h3 style={{ color: "#e6edf3", marginBottom: 16 }}>
        Conversiones — {currentUserId}
      </h3>

      <LineChart width={360} height={220} data={campaignData}>
        <CartesianGrid strokeDasharray="3 3" stroke="#30363d" />
        <XAxis dataKey="day" stroke="#8b949e" />
        <YAxis stroke="#8b949e" />
        <Tooltip />
        <Line dataKey="conversions" stroke="#3fb950" strokeWidth={2} />
        <Line dataKey="spend" stroke="#58a6ff" strokeWidth={2} />
      </LineChart>

      <button onClick={() => onExportCSV(campaignData)}>
        Exportar CSV
      </button>
    </div>
  );
}
app/(tabs)/analytics.tsx TSX · NATIVO
import { View, Text, Share } from "react-native";
import { useLocalSearchParams } from "expo-router";
import CampaignChart from "@/components/CampaignChart";  // DOM component

const CAMPAIGN_DATA = [
  { day: "Lun", conversions: 42, spend: 180 },
  { day: "Mar", conversions: 67, spend: 220 },
  { day: "Mié", conversions: 55, spend: 195 },
  { day: "Jue", conversions: 89, spend: 310 },
  { day: "Vie", conversions: 112, spend: 380 },
];

export default function AnalyticsScreen() {
  const { userId } = useLocalSearchParams();  // no disponible en DOM, pasar como prop

  return (
    <View style={{ flex: 1, background: "#0d1117" }}>
      <Text>CultivaDash Analytics</Text>

      <CampaignChart
        campaignData={CAMPAIGN_DATA}
        currentUserId={userId as string}
        onExportCSV={async (rows) => {
          await Share.share({ message: JSON.stringify(rows) });
        }}
        dom={{ style: { height: 320 }, scrollEnabled: false }}
      />
    </View>
  );
}
Vista previa del chart (recharts en WebView)

CultivaDash — Conversiones & Spend semana actual 'use dom'

0 100 200 300 400 Lun Mar Mié Jue Vie Viernes conv: 112 spend: 380
LEYENDA
Conversiones
Gasto (€)
Total sem.:
365 conv.
1.285 €
CPC: 3,52 €
Tres DOM Components para CultivaDash
CampaignChart
CampaignChart.tsx
Gráfico de líneas con recharts. Muestra conversiones y spend semanal. Callback onExportCSV llama a Share nativo.
recharts Share nativa
ApiCodeBlock
ApiCodeBlock.tsx
Syntax highlighting con react-syntax-highlighter para mostrar snippets del SDK de CULTIVA IA. Tema dark Dracula.
syntax-highlighter DOM only
CampaignNotes
CampaignNotes.tsx
Editor rich text (tiptap) para notas de campaña con HTML básico. Guarda via callback async a AsyncStorage nativo.
tiptap AsyncStorage
Comportamiento por plataforma
Plataforma Render dom prop Performance
iOS 16+ WKWebView aplicada 60fps para charts pequeños
Android 13+ WebView (Chromium) aplicada 60fps, mayor consumo RAM
Web (Expo Web) Directo en DOM ignorada Nativo del browser
Tips de implementacion
Mantén DOM components enfocados
No pongas pantallas enteras en WebViews. Usa componentes nativos para navegación y DOM components solo para contenido especializado.
🔄
Hot reload funciona en desarrollo
Los DOM components se actualizan automáticamente con Fast Refresh igual que componentes nativos durante el desarrollo.
🛣️
useLocalSearchParams → pasar como prop
Los hooks de routing no funcionan en DOM components. Lee el valor en el padre nativo y pásalo como prop serializable.
🎨
CSS en contexto aislado
Cada DOM component tiene su propio contexto JS. Incluye los imports de CSS dentro del mismo archivo del componente.