Dimensões e Layout Responsivo
useWindowDimensions, breakpoints e layouts para tablet/celular.
Busque em todas as páginas da documentação
useWindowDimensions, breakpoints e layouts para tablet/celular.
Cartão de receita de referência rápida - pronto para copiar e colar.
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" },
});Quando usar isso: Qualquer layout que precise se adaptar a mudanças de tamanho de celular vs tablet, retrato vs paisagem, ou tela dobrável - especialmente telas master-detail, grades e modais.
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: "Inbox", body: "3 novas mensagens" },
{ id: "2", title: "Rascunhos", body: "1 rascunho não enviado" },
{ id: "3", title: "Arquivo", body: "128 arquivados" },
];
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}>Email</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>
{/* Detalhe - lado a lado no tablet, oculto no celular (navegar separadamente no app real) */}
{isTablet && (
<ScrollView style={styles.detailPane} contentContainerStyle={styles.detailContent}>
<Text style={styles.detailTitle}>Selecione uma mensagem</Text>
<Text style={styles.detailBody}>
No tablet ({width}×{height}), lista e detalhe compartilham a tela.
</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 },
});O que isso demonstra:
useWindowDimensions() controlando a re-organização em rotação e redimensionamento.>= 768) alternando layout de linha vs coluna.SafeAreaView para layout ciente de notch/margens, separado do tamanho bruto da janela.useWindowDimensions se inscreve em eventos de mudança de dimensão e retorna { width, height, scale, fontScale }. Quando o usuário gira o dispositivo ou redimensiona uma janela de tela dividida, o hook aciona uma re-renderização com valores atualizados.Dimensions.get('window') retorna um instantâneo único. Ele não se inscreve em mudanças - bom para constantes no nível do módulo, errado para estado de UI responsivo.width: 100 tem aproximadamente o mesmo tamanho físico em uma tela 2x e 3x. PixelRatio.get() informa o multiplicador (geralmente 2 ou 3).fontScale - Reflete a configuração de tamanho de texto de acessibilidade do usuário. Respeite-o para o texto do corpo; evite fixar UI críticas em tamanhos de fonte fixos quando possível.useWindowDimensions vs Dimensions| API | Se inscreve em mudanças | Melhor para |
|---|---|---|
useWindowDimensions() | Sim - re-renderiza o componente | Layouts responsivos, grades, colunas condicionais |
Dimensions.get('window') | Não - apenas instantâneo | Registro único, configuração estática no carregamento do módulo |
Dimensions.addEventListener('change', …) | Assinatura manual | Padrão legado - prefira o hook |
import { Dimensions, useWindowDimensions, PixelRatio } from "react-native";
// Instantâneo - NÃO será atualizado na rotação
const { width: staticWidth } = Dimensions.get("window");
// Reativo - atualiza na rotação
function Layout() {
const { width, height, scale, fontScale } = useWindowDimensions();
const physicalWidth = width * PixelRatio.get();
// ...
}// Baseado na largura (mais comum)
const isTablet = width >= 768;
// Orientação
const isLandscape = width > height;
// Dimensão mínima (útil para dobráveis - tipo tablet quando qualquer eixo é grande)
const minDim = Math.min(width, height);
const isTabletClass = minDim >= 600;
// Plataforma + largura (iPad sempre relata largura grande; tablets Android variam)
import { Platform } from "react-native";
const isIPad = Platform.OS === "ios" && Platform.isPad;| Padrão | Celular | Tablet |
|---|---|---|
| Master-detail | Navegação em pilha - lista leva para o detalhe | Visão dividida - lista + detalhe lado a lado |
| Colunas de grade | 1–2 colunas | 2–4 colunas |
| Modal | Folha de tela inteira | Diálogo centralizado (maxWidth: 560) |
| Navegação | Abas inferiores | Barra lateral ou barra lateral permanente |
As dimensões da janela incluem a tela inteira. As margens da área segura excluem notches, barras de status e indicadores de início:
import { useSafeAreaInsets } from "react-native-safe-area-context";
function Header() {
const insets = useSafeAreaInsets();
return <View style={{ paddingTop: insets.top, paddingHorizontal: 16 }} />;
}Não confunda width de useWindowDimensions com a área de conteúdo utilizável - subtraia margens e qualquer cromo do aplicativo (barras de abas, cabeçalhos).
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() em renderização sem assinatura - A UI permanece no layout retrato após girar para paisagem. Correção: Use useWindowDimensions() ou Dimensions.addEventListener.width: 375 codificados - iPhones variam de 320 a 430+ de largura lógica; Android é ainda mais largo. Correção: Larguras percentuais, flex e contagens de colunas baseadas em breakpoints.fontScale - Usuários com texto de acessibilidade grande veem rótulos cortados. Correção: Permita que o texto quebre, use flexShrink: 1 e teste com fonte do sistema ampliada.Dimensions.get('screen') inclui áreas sob barras de UI do sistema; window é geralmente correto para layout. Correção: Use 'window' por padrão, a menos que você tenha um motivo específico para 'screen'.Platform.isPad não ajuda em tablets Android. Correção: Breakpoint de largura (>= 768) funciona em várias plataformas.min(width, height) ou teste estados de janela estreita, não apenas a classe do dispositivo.paddingTop: insets.top dentro de uma tela já envolvida em SafeAreaView com edges={['top']}. Correção: Escolha um proprietário para o preenchimento da área segura por eixo.| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
useWindowDimensions | Padrão para componentes React responsivos | Você precisa de valores de dimensão fora de um componente (use instantâneo Dimensions + listener) |
Larguras percentuais (width: '50%') | Divisões simples de duas colunas | Calhas de pixel precisas e matemática de várias colunas (use dp calculado) |
flex / flexWrap | Grades fluidas sem breakpoints explícitos | Master-detail estrito que não deve compartilhar uma linha no celular |
react-native-responsive-screen (comunitário) | Equipes que desejam helpers widthPercentageToDP | Você prefere zero dependências extras - hooks + constantes são suficientes |
Arquivos específicos da plataforma (*.tablet.tsx) | UIs de tablet radicalmente diferentes | Pequenos ajustes de espaçamento (if/else de breakpoint é mais simples) |
Dimensions.get() retorna um instantâneo no momento da chamada. Ele não re-renderiza seu componente quando o usuário gira o dispositivo ou redimensiona a tela dividida. useWindowDimensions é um hook que se inscreve em mudanças de dimensão e aciona uma re-renderização automaticamente.
window - a viewport do aplicativo utilizável (o que a maioria dos layouts precisa).screen - a tela física completa, incluindo áreas sob barras de status/navegação em alguns dispositivos Android.Use window por padrão, a menos que você tenha um caso de uso específico de tela inteira.
768 é a convenção mais comum (o retrato do iPad é 768 dp). Algumas equipes usam 600 (limiar de "tablet" do Material) ou 1024 para layouts de classe desktop. Escolha um, documente-o e use-o consistentemente.
import { PixelRatio } from "react-native";
const physicalPixels = layoutDp * PixelRatio.get();Uma caixa de 100×100 dp tem 200×200 pixels físicos em um dispositivo 2x e 300×300 em um dispositivo 3x.
No tablet (width >= 768), renderize a lista e o detalhe em um contêiner flexDirection: 'row'. No celular, use Expo Router / React Navigation stack - a tela da lista empurra a tela de detalhes. Passe a mesma camada de dados para ambos os layouts.
Sim. É um hook padrão do React Native. Use-o dentro de qualquer tela ou layout de componente cliente. Ele atualiza quando a tela montada pelo roteador gira ou redimensiona.
A tela dividida encolhe drasticamente window.width. Um breakpoint de largura de tablet pode não ser acionado mesmo em um dispositivo grande. Teste larguras estreitas e prefira layouts baseados em flex que degradam graciosamente.
Use expo-screen-orientation para bloquear ou desbloquear a orientação por tela. Mesmo com um bloqueio, sempre escreva layouts defensivamente - as políticas do sistema operacional e os dispositivos dobráveis ainda podem alterar o tamanho efetivo da viewport.
Platform.isPad é confiável no iOS, mas não tem um equivalente no Android. Para layouts de tablet multiplataforma, breakpoints de largura são a escolha portátil. Platform.isPad é bom para ramificação apenas no iOS (por exemplo, popover vs sheet).
Os usuários podem aumentar o texto do sistema nas configurações de acessibilidade. fontScale em useWindowDimensions reflete esse multiplicador. Evite linhas de altura fixa que cortam texto ampliado; prefira quebra e flexShrink.
Não nativamente. Simule-os com useWindowDimensions + constantes de breakpoint, ou use bibliotecas como react-native-media-query em destinos web. Para iOS/Android, breakpoints baseados em hooks são o padrão.
const { width } = useWindowDimensions();
const modalWidth = Math.min(width - 48, 560);
<View style={{ width: modalWidth, alignSelf: "center" }} />scale é a proporção de pixels (o mesmo que PixelRatio.get()). fontScale é o fator de escala de texto de acessibilidade do usuário. Ambos estão incluídos no valor de retorno do hook.
Defina as dimensões da imagem em relação à largura da coluna (calculada a partir de useWindowDimensions) em vez de pixels globais fixos. Veja Imagens e Recursos para orientação de dimensionamento de recursos.
View, flex e StyleSheetPlatform.isPad e arquivos de layout por plataformaVersões da Stack: Esta página foi escrita para React 19.2.3, React Native 0.86.0 e Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última atualização: 19 de jul. de 2026