Dimensiones y Diseño Responsivo
useWindowDimensions, puntos de quiebre y diseños tablet/teléfono.
Busca en todas las páginas de la documentación
useWindowDimensions, puntos de quiebre y diseños tablet/teléfono.
Tarjeta de referencia rápida - lista para copiar y pegar.
import { useMemo } from "react";
import { useWindowDimensions, View, Text, StyleSheet } from "react-native";
const BREAKPOINTS = { phone: 0, tablet: 768, desktop: 1024 } as const;
function useBreakpoint() {
const { width } = useWindowDimensions();
if (width >= BREAKPOINTS.desktop) return "desktop" as const;
if (width >= BREAKPOINTS.tablet) return "tablet" as const;
return "phone" as const;
}
export function ResponsiveGrid({ children }: { children: React.ReactNode }) {
const { width } = useWindowDimensions();
const bp = useBreakpoint();
const columns = bp === "phone" ? 1 : bp === "tablet" ? 2 : 3;
const gap = width < BREAKPOINTS.tablet ? 12 : 16;
const itemWidth = useMemo(
() => (width - gap * (columns + 1)) / columns,
[width, columns, gap]
);
return (
<View style={[styles.grid, { gap, padding: gap }]}>
{Array.isArray(children)
? children.map((child, i) => (
<View key={i} style={{ width: itemWidth }}>
{child}
</View>
))
: children}
</View>
);
}
const styles = StyleSheet.create({
grid: { flexDirection: "row", flexWrap: "wrap" },
});Cuándo usarlo: Cualquier diseño que deba adaptarse a teléfono vs tablet, vertical vs horizontal, o cambios de tamaño de pantalla plegable - especialmente en pantallas master-detail, grillas y modales.
import { ScrollView, StyleSheet, Text, View, useWindowDimensions } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
const TABLET_MIN = 768;
type Item = { id: string; title: string; body: string };
const ITEMS: Item[] = [
{ id: "1", title: "Bandeja de entrada", body: "3 mensajes nuevos" },
{ id: "2", title: "Borradores", body: "1 borrador sin enviar" },
{ id: "3", title: "Archivo", body: "128 archivados" },
];
export default function MailScreen() {
const { width, height } = useWindowDimensions();
const isTablet = width >= TABLET_MIN;
const isLandscape = width > height;
return (
<SafeAreaView style={styles.safe} edges={["top", "left", "right"]}>
<View style={[styles.shell, isTablet && styles.shellTablet]}>
{/* Barra lateral / lista */}
<View
style={[
styles.listPane,
isTablet ? { width: isLandscape ? 320 : 280 } : styles.listPanePhone,
]}
>
<Text style={styles.heading}>Correo</Text>
{ITEMS.map((item) => (
<View key={item.id} style={styles.listRow}>
<Text style={styles.rowTitle}>{item.title}</Text>
<Text style={styles.rowBody} numberOfLines={1}>
{item.body}
</Text>
</View>
))}
</View>
{/* Detalle - lado a lado en tablet, oculto en teléfono (navegar por separado en app real) */}
{isTablet && (
<ScrollView style={styles.detailPane} contentContainerStyle={styles.detailContent}>
<Text style={styles.detailTitle}>Selecciona un mensaje</Text>
<Text style={styles.detailBody}>
En tablet ({width}x{height}), la lista y detalle comparten la pantalla.
</Text>
</ScrollView>
)}
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safe: { flex: 1, backgroundColor: "#fff" },
shell: { flex: 1 },
shellTablet: { flexDirection: "row" },
listPane: { borderRightWidth: StyleSheet.hairlineWidth, borderColor: "#ddd" },
listPanePhone: { flex: 1 },
heading: { fontSize: 28, fontWeight: "700", padding: 16 },
listRow: { paddingHorizontal: 16, paddingVertical: 12, borderBottomWidth: StyleSheet.hairlineWidth, borderColor: "#eee" },
rowTitle: { fontSize: 16, fontWeight: "600" },
rowBody: { fontSize: 14, color: "#666", marginTop: 2 },
detailPane: { flex: 1 },
detailContent: { padding: 24 },
detailTitle: { fontSize: 22, fontWeight: "600" },
detailBody: { fontSize: 16, color: "#444", marginTop: 8, lineHeight: 24 },
});Lo que esto demuestra:
useWindowDimensions() manejando re-diseño en rotación y cambio de tamaño.>= 768) alternando diseño en fila vs columna.SafeAreaView para diseño consciente de muesca/inset separado del tamaño de ventana sin procesar.useWindowDimensions se suscribe a eventos de cambio de dimensión y devuelve { width, height, scale, fontScale }. Cuando el usuario rota el dispositivo o redimensiona una ventana de pantalla dividida, el hook provoca un re-renderizado con valores actualizados.Dimensions.get('window') devuelve una instantánea de una sola vez. No se suscribe a cambios - está bien para constantes de nivel de módulo, incorrecto para UI responsiva.width: 100 tiene aproximadamente el mismo tamaño físico en una pantalla 2x y 3x. PixelRatio.get() te dice el multiplicador (típicamente 2 o 3).fontScale - Refleja la configuración de tamaño de texto de accesibilidad del usuario. Respétalo para texto del cuerpo; evita bloquear UI crítica a tamaños de fuente fijos cuando sea posible.useWindowDimensions vs Dimensions| API | Se suscribe a cambios | Mejor para |
|---|---|---|
useWindowDimensions() | Sí - hace re-renderizado del componente | Diseños responsivos, grillas, columnas condicionales |
Dimensions.get('window') | No - solo instantánea | Registro de una sola vez, config estática en carga de módulo |
Dimensions.addEventListener('change', …) | Suscripción manual | Patrón heredado - prefiere el hook |
import { Dimensions, useWindowDimensions, PixelRatio } from "react-native";
// Instantánea - NO se actualizará en rotación
const { width: staticWidth } = Dimensions.get("window");
// Reactivo - se actualiza en rotación
function Layout() {
const { width, height, scale, fontScale } = useWindowDimensions();
const physicalWidth = width * PixelRatio.get();
// ...
}// Basado en ancho (más común)
const isTablet = width >= 768;
// Orientación
const isLandscape = width > height;
// Dimensión mínima (útil para plegables - tablet-ish cuando cualquier eje es grande)
const minDim = Math.min(width, height);
const isTabletClass = minDim >= 600;
// Plataforma + ancho (iPad siempre reporta ancho grande; tablets Android varían)
import { Platform } from "react-native";
const isIPad = Platform.OS === "ios" && Platform.isPad;| Patrón | Teléfono | Tablet |
|---|---|---|
| Master-detail | Navegación en pila - lista empuja a detalle | Vista dividida - lista + detalle lado a lado |
| Columnas de grilla | 1-2 columnas | 2-4 columnas |
| Modal | Hoja a pantalla completa | Diálogo centrado (maxWidth: 560) |
| Navegación | Pestañas inferiores | Carril lateral o barra lateral permanente |
Las dimensiones de la ventana incluyen la pantalla completa. Los insets de área segura excluyen muescas, barras de estado e indicadores de inicio:
import { useSafeAreaInsets } from "react-native-safe-area-context";
function Header() {
const insets = useSafeAreaInsets();
return <View style={{ paddingTop: insets.top, paddingHorizontal: 16 }} />;
}No confundas width de useWindowDimensions con el área de contenido utilizable - resta insets y cualquier interfaz de la app (barras de pestañas, encabezados).
import { useWindowDimensions, ScaledSize } from "react-native";
type Breakpoint = "phone" | "tablet" | "desktop";
function getBreakpoint(window: ScaledSize): Breakpoint {
if (window.width >= 1024) return "desktop";
if (window.width >= 768) return "tablet";
return "phone";
}
function useResponsiveValue<T>(values: Record<Breakpoint, T>): T {
const window = useWindowDimensions();
return values[getBreakpoint(window)];
}
// Uso
const padding = useResponsiveValue({ phone: 16, tablet: 24, desktop: 32 });Dimensions.get() en render sin suscripción - UI permanece en diseño vertical después de rotar a horizontal. Solución: Usa useWindowDimensions() o Dimensions.addEventListener.width: 375 codificados - Los iPhones van desde 320 a 430+ ancho lógico; Android es más ancho. Solución: Anchos de porcentaje, flex, y conteos de columnas basados en punto de quiebre.fontScale - Los usuarios con texto de accesibilidad grande ven etiquetas recortadas. Solución: Permite que el texto se envuelva, usa flexShrink: 1, y prueba con fuente del sistema ampliada.Dimensions.get('screen') incluye áreas bajo UI del sistema; window usualmente es correcto para diseño. Solución: Por defecto a 'window' a menos que tengas una razón específica para 'screen'.Platform.isPad no ayuda en tablets Android. Solución: El punto de quiebre de ancho (>= 768) funciona multiplataforma.min(width, height) o prueba estados de ventana estrecha, no solo clase de dispositivo.paddingTop: insets.top dentro de una pantalla ya envuelta en SafeAreaView con edges={['top']}. Solución: Elige un propietario para relleno de área segura por eje.| Alternativa | Úsalo cuando | No lo uses cuando |
|---|---|---|
useWindowDimensions | Predeterminado para componentes React responsivos | Necesitas valores de dimensión fuera de un componente (usa instantánea Dimensions + listener) |
Anchos de porcentaje (width: '50%') | Divisiones simples de dos columnas | Márgenes de píxeles precisos y matemática multicolumna (usa dp calculado) |
flex / flexWrap | Grillas fluidas sin puntos de quiebre explícitos | Master-detail estricto que no debe compartir una fila en teléfono |
react-native-responsive-screen (comunidad) | Equipos queriendo ayudantes widthPercentageToDP | Prefieres cero deps extra - hooks + constantes son suficientes |
Archivos específicos de plataforma (*.tablet.tsx) | UIs tablet radicalmente diferentes | Ajustes menores de espaciado (punto de quiebre if/else es más simple) |
Dimensions.get() devuelve una instantánea en el momento de la llamada. No hace re-renderizado de tu componente cuando el usuario rota el dispositivo o redimensiona la pantalla dividida. useWindowDimensions es un hook que se suscribe a cambios de dimensión y provoca un re-renderizado automáticamente.
window - el viewport de app utilizable (lo que la mayoría de diseños necesitan).screen - la pantalla física completa, incluyendo áreas bajo barras de estado/navegación en algunos dispositivos Android.Por defecto usa window a menos que tengas un caso de uso de pantalla completa específico.
768 es la convención más común (iPad en vertical es 768 dp). Algunos equipos usan 600 (umbral tablet de Material) o 1024 para diseños de clase desktop. Elige uno, documéntalo, y úsalo consistentemente.
import { PixelRatio } from "react-native";
const physicalPixels = layoutDp * PixelRatio.get();Una caja 100x100 dp es 200x200 píxeles físicos en un dispositivo 2x y 300x300 en un dispositivo 3x.
En tablet (width >= 768), renderiza lista y detalle en un contenedor flexDirection: 'row'. En teléfono, usa Expo Router / React Navigation stack - la pantalla de lista empuja a la pantalla de detalle. Pasa la misma capa de datos a ambos diseños.
Sí. Es un hook React Native estándar. Úsalo dentro de cualquier pantalla o diseño de componente cliente. Se actualiza cuando la pantalla montada en el router rota o se redimensiona.
La pantalla dividida reduce window.width dramáticamente. Un punto de quiebre de ancho tablet puede no dispararse incluso en un dispositivo grande. Prueba anchos estrechos y prefiere diseños basados en flex que se degraden gracefully.
Usa expo-screen-orientation para bloquear o desbloquear orientación por pantalla. Incluso con un bloqueo, siempre escribe diseños defensivamente - políticas del SO y plegables pueden seguir cambiando el tamaño de viewport efectivo.
Platform.isPad es confiable en iOS pero no tiene equivalente Android. Para diseños de tablet multiplataforma, los puntos de quiebre de ancho son la opción portátil. Platform.isPad está bien para ramificación solo iOS (p.ej., popover vs sheet).
Los usuarios pueden ampliar el texto del sistema en configuración de accesibilidad. fontScale en useWindowDimensions refleja este multiplicador. Evita filas de altura fija que recorten texto ampliado; prefiere envolvimiento y flexShrink.
No nativamente. Simúlalas con useWindowDimensions + constantes de punto de quiebre, o usa librerías como react-native-media-query en objetivos web. Para iOS/Android, los puntos de quiebre basados en hook son el patrón estándar.
const { width } = useWindowDimensions();
const modalWidth = Math.min(width - 48, 560);
<View style={{ width: modalWidth, alignSelf: "center" }} />scale es la relación de píxeles (igual que PixelRatio.get()). fontScale es el factor de escalado de texto de accesibilidad del usuario. Ambos se incluyen en el valor de retorno del hook.
Establece dimensiones de imagen relativas al ancho de columna (calculado desde useWindowDimensions) en lugar de píxeles globales fijos. Ve Imágenes y Activos para guía de dimensionamiento de activos.
View, flex y StyleSheetPlatform.isPad y archivos de diseño por plataformaVersiones de Stack: Esta página fue escrita para React 19.2.3, React Native 0.86.0, y Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última actualización: 19 jul 2026