Escala de tipografía y carga de fuentes
expo-font, familias personalizadas y dimensionamiento de texto accesible.
Busca en todas las páginas de la documentación
expo-font, familias personalizadas y dimensionamiento de texto accesible.
Tarjeta de referencia rápida - lista para copiar y pegar.
npx expo install expo-font expo-splash-screen// app/_layout.tsx (raíz de Expo Router) o App.tsx
import { useFonts } from "expo-font";
import * as SplashScreen from "expo-splash-screen";
import { useEffect } from "react";
import { Text, View } from "react-native";
import { typography } from "./theme/typography";
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
const [loaded, error] = useFonts({
"Inter-Regular": require("./assets/fonts/Inter-Regular.ttf"),
"Inter-SemiBold": require("./assets/fonts/Inter-SemiBold.ttf"),
});
useEffect(() => {
if (loaded || error) SplashScreen.hideAsync();
}, [loaded, error]);
if (!loaded && !error) return null;
return (
<View style={{ flex: 1 }}>
<Text style={typography.title}>Tipografía lista</Text>
<Text style={typography.body}>Inter se carga antes del primer renderizado.</Text>
</View>
);
}// theme/typography.ts
import { StyleSheet } from "react-native";
export const typography = StyleSheet.create({
title: {
fontFamily: "Inter-SemiBold",
fontSize: 24,
lineHeight: 32,
letterSpacing: -0.3,
color: "#0f172a",
},
body: {
fontFamily: "Inter-Regular",
fontSize: 16,
lineHeight: 24,
color: "#334155",
},
caption: {
fontFamily: "Inter-Regular",
fontSize: 12,
lineHeight: 16,
color: "#64748b",
},
});Cuándo usarlo: Fuentes de marca, jerarquía de encabezados consistente, o texto accesible - necesitas familias personalizadas cargadas antes de que se renderice la UI y una escala de tokens en lugar de números mágicos esparcidos en pantallas.
import { useFonts } from "expo-font";
import * as SplashScreen from "expo-splash-screen";
import { useEffect } from "react";
import { PixelRatio, StyleSheet, Text, View } from "react-native";
SplashScreen.preventAutoHideAsync();
const FONT_SCALE = PixelRatio.getFontScale();
type TextVariant = "display" | "title" | "body" | "caption" | "label";
const variantStyles: Record<TextVariant, object> = {
display: typography.display,
title: typography.title,
body: typography.body,
caption: typography.caption,
label: typography.label,
};
function AppText({
variant = "body",
children,
maxScale = 1.35,
}: {
variant?: TextVariant;
children: React.ReactNode;
maxScale?: number;
}) {
return (
<Text
style={variantStyles[variant]}
allowFontScaling
maxFontSizeMultiplier={maxScale}
>
{children}
</Text>
);
}
export default function TypographyScreen() {
const [fontsLoaded, fontError] = useFonts({
"Inter-Regular": require("./assets/fonts/Inter-Regular.ttf"),
"Inter-SemiBold": require("./assets/fonts/Inter-SemiBold.ttf"),
"Inter-Bold": require("./assets/fonts/Inter-Bold.ttf"),
});
useEffect(() => {
if (fontsLoaded || fontError) SplashScreen.hideAsync();
}, [fontsLoaded, fontError]);
if (!fontsLoaded && !fontError) return null;
return (
<View style={styles.screen}>
<AppText variant="display">Buenos días</AppText>
<AppText variant="title">Tus tareas hoy</AppText>
<AppText variant="body">
La escala de fuente del sistema es {FONT_SCALE.toFixed(2)}×. El texto del cuerpo se escala con la configuración de accesibilidad hasta el límite configurado.
</AppText>
<AppText variant="caption">Última sincronización hace 2 minutos</AppText>
<View style={styles.chip}>
<AppText variant="label" maxScale={1.2}>
3 vencidas
</AppText>
</View>
</View>
);
}
const typography = StyleSheet.create({
display: {
fontFamily: "Inter-Bold",
fontSize: 34,
lineHeight: 40,
letterSpacing: -0.5,
color: "#0f172a",
},
title: {
fontFamily: "Inter-SemiBold",
fontSize: 22,
lineHeight: 28,
color: "#0f172a",
},
body: {
fontFamily: "Inter-Regular",
fontSize: 16,
lineHeight: 24,
color: "#334155",
},
caption: {
fontFamily: "Inter-Regular",
fontSize: 13,
lineHeight: 18,
color: "#64748b",
},
label: {
fontFamily: "Inter-SemiBold",
fontSize: 12,
lineHeight: 16,
letterSpacing: 0.6,
textTransform: "uppercase",
color: "#475569",
},
});
const styles = StyleSheet.create({
screen: { flex: 1, padding: 24, gap: 12, backgroundColor: "#f8fafc" },
chip: {
alignSelf: "flex-start",
marginTop: 8,
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: 6,
backgroundColor: "#fee2e2",
},
});Lo que esto demuestra:
useFonts carga múltiples pesos como claves de familia separadas - el patrón de Expo SDK 57 para archivos TTF estáticosdisplay, title, body, caption, label) centralizan el tamaño, altura de línea y familiaAppText aplica allowFontScaling y límites de maxFontSizeMultiplier por variantePixelRatio.getFontScale() expone el multiplicador de accesibilidad actual para depuración de diseñoexpo-font se envía con Expo SDK 57 y envuelve el registro de fuentes nativo en iOS y Android.
| API | Caso de uso |
|---|---|
useFonts(map) | Hook de React - devuelve [loaded, error]; mejor para bloquear la raíz del layout |
Font.loadAsync(map) | Precarga imperativa en rutinas de arranque junto con Asset.loadAsync |
Font.isLoaded(name) | Verifica una familia única antes de renderizar un subárbol lazy |
import * as Font from "expo-font";
// Carga imperativa en lote (p. ej. antes de que se monte la navegación)
await Font.loadAsync({
"Inter-Regular": require("./assets/fonts/Inter-Regular.ttf"),
});Instala alineado con SDK 57:
npx expo install expo-font expo-splash-screenLos archivos de fuente deben vivir bajo assets/ y ser require()'d estáticamente - Metro los agrupa en tiempo de compilación.
Sin una puerta, React renderiza inmediatamente con la fuente del sistema, luego cambia cuando llegan las fuentes personalizadas (flash de texto sin estilo).
import * as SplashScreen from "expo-splash-screen";
SplashScreen.preventAutoHideAsync();
// En el componente raíz
useEffect(() => {
if (loaded || error) SplashScreen.hideAsync();
}, [loaded, error]);
if (!loaded && !error) return null;preventAutoHideAsync en el alcance del módulo antes del primer renderizado.null (o un marcador de posición mínimo) mientras loaded es false.React Native no asigna automáticamente fontWeight: "600" a un TTF personalizado. Cada peso/estilo suele ser un archivo separado registrado bajo su propio nombre de familia.
// Funciona - familia explícita por archivo
{ fontFamily: "Inter-SemiBold", fontSize: 18 }
// No confiable con familia personalizada - puede volver a la negrita del sistema
{ fontFamily: "Inter-Regular", fontWeight: "600", fontSize: 18 }| Enfoque | Pros | Contras |
|---|---|---|
Archivos separados por peso (Inter-Bold, etc.) | Predecible en iOS y Android | Más archivos para cargar |
| Fuente variable única (cuando se admite) | Un archivo | Verifica la compatibilidad de RN/Expo con tu formato de fuente |
Fuente del sistema (fontFamily: undefined) | Cero tiempo de carga, sensación nativa | Sin tipografía de marca |
Define una rampa fija - no elijas fontSize por pantalla ad hoc.
| Token | fontSize típico / lineHeight | Uso |
|---|---|---|
display | 32-40 / 38-48 | Encabezados de héroe |
title | 20-24 / 26-32 | Títulos de pantalla, encabezados de tarjetas |
body | 15-17 / 22-26 | Párrafos, texto primario de lista |
caption | 11-13 / 14-18 | Marcas de tiempo, metadatos |
label | 11-12 / 14-16 | Chips, distintivos, líneas superiores |
export const fontSize = {
xs: 12,
sm: 14,
md: 16,
lg: 20,
xl: 24,
"2xl": 32,
} as const;
// lineHeight ≈ 1.4–1.5 × fontSize para cuerpo; más ajustado para displayEmpareja tokens con letterSpacing y textTransform en etiquetas - mantén el ritmo numérico en un módulo.
iOS y Android exponen configuraciones de escala de fuente de accesibilidad. React Native las respeta por defecto en Text.
| Prop | Predeterminado | Propósito |
|---|---|---|
allowFontScaling | true | Cuando false, ignora la escala de fuente del sistema |
maxFontSizeMultiplier | ilimitado | Limita qué tan grande puede crecer el texto (iOS/Android) |
adjustsFontSizeToFit | false | Reduce el texto para que quepa en la caja numberOfLines (iOS) |
<Text allowFontScaling maxFontSizeMultiplier={1.3} style={typography.body}>
Copia de cuerpo accesible
</Text>
// UI densa - barra de herramientas, etiquetas de pestaña
<Text maxFontSizeMultiplier={1.1} numberOfLines={1}>
Panel de control
</Text>Usa PixelRatio.getFontScale() para registrar o adaptar diseños en escalas extremas (p. ej. cambiar filas de lista a dos líneas).
No desactives allowFontScaling en toda la aplicación - rompe la accesibilidad. Limita selectivamente en chips, barras de navegación y botones de altura fija.
lineHeight está en píxeles independientes de la densidad, no sin unidades como CSS line-height: 1.5.lineHeight = fontSize × 1.4 para cuerpo, más ajustado para tipo de pantalla grande.Text dentro de una fila con alignItems: "center" puede verse mal si las alturas de línea difieren - normaliza variantes.body: {
fontSize: 16,
lineHeight: 24, // 1.5× - párrafo cómodo
},
display: {
fontSize: 34,
lineHeight: 40, // ~1.18× - héroe ajustado
},Carga fuentes en la misma rutina de arranque que la precarga de imágenes:
import { Asset } from "expo-asset";
import * as Font from "expo-font";
export async function loadResources() {
await Promise.all([
Asset.loadAsync([require("./assets/logo.png")]),
Font.loadAsync({
"Inter-Regular": require("./assets/fonts/Inter-Regular.ttf"),
}),
]);
}Ver el artículo de imágenes y activos para coordinación de splash con Asset.loadAsync.
import type { TextStyle } from "react-native";
export type TypographyVariant = "display" | "title" | "body" | "caption";
export const typography: Record<TypographyVariant, TextStyle> = StyleSheet.create({
display: { fontFamily: "Inter-Bold", fontSize: 34, lineHeight: 40 },
title: { fontFamily: "Inter-SemiBold", fontSize: 22, lineHeight: 28 },
body: { fontFamily: "Inter-Regular", fontSize: 16, lineHeight: 24 },
caption: { fontFamily: "Inter-Regular", fontSize: 13, lineHeight: 18 },
});TextStyle incluye fontFamily, fontSize, lineHeight, letterSpacing, fontVariant.useFonts son strings - usa el mismo string en fontFamily.require() de .ttf / .otf devuelve un número (ID de módulo de activo) en tiempo de compilación.Flash de fuente del sistema - Renderizar texto antes de que useFonts se resuelva. Solución: Bloquea el layout raíz con pantalla de splash + return null hasta que loaded sea true.
fontWeight con familia personalizada - fontFamily: "Inter" + fontWeight: "700" a menudo ignora el archivo personalizado. Solución: Registra cada peso como su propio nombre de familia y referencia directo.
Cadena de familia de fuentes incorrecta - fontFamily debe coincidir exactamente con la clave de useFonts ("Inter-SemiBold", no "Inter SemiBold"). Solución: Copia claves del mapa de carga.
Archivo de fuente faltante en el paquete - Las rutas require dinámicas fallan en el análisis estático de Metro. Solución: Solo require("./assets/fonts/Inter-Regular.ttf") estático.
Escalado sin límite rompe diseños - Las fuentes de accesibilidad grandes desbordan barras de herramientas y chips. Solución: maxFontSizeMultiplier en componentes densos; permite escalado completo en copia de cuerpo.
lineHeight demasiado pequeño - Los descendientes se cortan en Android con alturas de línea ajustadas. Solución: Aumenta lineHeight o agrega paddingVertical en distintivos de una sola línea.
Splash nunca se oculta en error de fuente - Esperando solo loaded. Solución: if (loaded || error) SplashScreen.hideAsync() y muestra una UI de fuente del sistema de reserva.
Suposiciones de fuente variable - No todos los archivos de fuente variable funcionan de forma idéntica entre plataformas. Solución: Prueba en dispositivos iOS y Android; vuelve a pesos estáticos si los pesos no se resuelven.
| Alternativa | Usarla cuando | No usar cuando |
|---|---|---|
Hook useFonts en raíz | Expo Router / layout raíz único | Fuentes necesarias solo en una pantalla lazy (aún así precarga en raíz para consistencia) |
Font.loadAsync en arranque | Cargador compartido con Asset.loadAsync | Prefieres bloqueo de splash impulsado por hook |
| Solo fuentes del sistema | Utilidades, herramientas internas, envío más rápido | Las directrices de marca requieren tipo personalizado |
Paquetes @expo-google-fonts/* | Fuentes populares abiertas sin agrupar archivos | Fuentes con licencia corporativa no en Google Fonts |
expo-google-fonts + useFonts | Configuración rápida de Inter/Roboto | Ya envías activos OTF/TTF con licencia |
| Tokens de texto de Tamagui / NativeWind | Sistema de diseño con tipografía temática | Una pantalla con dos estilos de texto |
npx expo install expo-font expo-splash-screenexpo-font se incluye en Expo SDK - expo install fija una versión compatible.useFonts desde expo-font en tu layout raíz..ttf o .otf bajo assets/fonts/ y haz require() estático de ellos.useFonts es un hook de React que dispara re-renderizado cuando se completa la carga - ideal para árboles de componentes.Font.loadAsync devuelve una Promise - ideal para arranque imperativo antes de registerRootComponent.require().useFonts."Inter-Bold" en fontFamily, no "Inter" con fontWeight: "700".SplashScreen.preventAutoHideAsync();
// ...
if (!loaded && !error) return null;Text de marca hasta que loaded sea true.fontSize y lineHeight emparejados.theme/typography.ts como StyleSheet.create u objetos simples.fontSize: 17.true para casi todo el texto dirigido al usuario - respeta la configuración de accesibilidad.false solo para texto decorativo o iconos de tamaño fijo implementados como texto (raro).maxFontSizeMultiplier sobre desactivar el escalado completamente.1.0 significa sin escalado; 1.3 permite hasta 30% más grande que el fontSize del estilo.1.5 o superior.fontFamily.require() estática.fontWeight en lugar del archivo de familia correcto - carga y referencia cada peso explícitamente.lineHeight: 24 con fontSize: 16.fontSize; encabezados de visualización: ~1.1-1.2×.lineHeight o agrega relleno vertical.@expo-google-fonts/inter envuelven useFonts con activos pre-agrupados.npx expo install @expo-google-fonts/inter expo-font.require() local de archivos OTF/TTF.numberOfLines, flexShrink: 1 y barras de herramientas de múltiples líneas en escalas altas.PixelRatio.getFontScale() ayuda a depurar el diseño en la configuración de accesibilidad.numberOfLines - principalmente iOS.const typography = StyleSheet.create({ ... }) satisfies Record<string, TextStyle>;type Variant = keyof typeof typography.variant: Variant para autocompletado.Text anidado hereda fontSize, fontFamily, color y lineHeight del padre a menos que se anulen.fontFamily si el estilo padre de cuerpo ya lo establece.StyleSheet.create y composición de estilo de textouseFonts junto con Asset.loadAsyncTextVersiones 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