Let’s be real, hardcoding fontSize: 16
in your React Native app might look okay on your test device... but then your friend opens it on their monster-sized phone and it's barely readable. Or worse, your layout breaks.
🌟 The problem? Font sizes in React Native don’t scale by default.
In this article, I’ll walk you through how to fix that once and for all using:
- A simple
responsiveFontSize()
utility, - A reusable custom
<Text>
component
Let’s make your typography look 🔥 on every screen.
💥 The Problem with Static Font Sizes
Take this simple line of code:
<Text style={{ fontSize: 16 }}>Hello world!</Text>
On an iPhone 11? Great.
On an iPhone 16 Pro Max? It looks like a tiny label from 2012.
On a budget Android? It might get chopped off (chopped 🫢).
We’re designing for a huge range of screen sizes and pixel densities, and static font sizes just don’t cut it anymore.
✅ The Fix: Responsive Font Scaling
Here’s a lightweight utility that scales your fonts based on screen width—without making them too big or too small:
import { Dimensions, PixelRatio } from "react-native";
const { width: SCREEN_WIDTH } = Dimensions.get("window");
// Base width from iPhone 11
const BASE_WIDTH = 375;
const widthScale = SCREEN_WIDTH / BASE_WIDTH;
const moderateScale = (size: number, factor = 0.5) =>
size + (widthScale * size - size) * factor;
export const responsiveFontSize = (fontSize: number, factor = 0.5) => {
const newSize = moderateScale(fontSize, factor);
return Math.round(PixelRatio.roundToNearestPixel(newSize));
};
💡 Why not scale based on height too?
In most UI design systems, width-based scaling produces more consistent visual results across devices. It’s also more stable when handling orientation changes.
🧪 Visual Comparison — Static vs Responsive Fonts
Here’s how font sizes behave across two common devices using static vs responsive sizing.
iPhone 11 (375 × 812)
Static Font (fontSize: 16 ) |
Responsive Font (responsiveFontSize(16) ) |
---|---|
![]() |
![]() |
iPhone 16 Pro Max (430 × 932)
Static Font (fontSize: 16 ) |
Responsive Font (responsiveFontSize(16) ) |
---|---|
![]() |
![]() |
🌟 Takeaway: On larger screens, the static font looks awkwardly small. But with responsive sizing, everything just feels... right.
🤩 Make It Reusable: A Custom <Text />
Component
Let’s wrap this into a flexible Text
component that your whole app can use:
import { forwardRef } from "react";
import { Text as DefaultText } from "react-native";
import { Colors, setOpacity } from "@/constants/colors";
import {
Typography,
FontWeight,
TextSize,
} from "@/constants/typography";
type RNTextProps = DefaultText["props"];
export interface TextProps extends RNTextProps {
size?: TextSize | number;
weight?: FontWeight;
dimRate?: `${number}%`;
textTransform?: "none" | "capitalize" | "uppercase" | "lowercase";
}
export const Text = forwardRef<DefaultText, TextProps>((props, ref) => {
const {
size = "small",
children,
weight = "regular",
textTransform = "none",
dimRate,
style,
...otherProps
} = props;
const getFontSize = () => {
if (typeof size === "number") return size;
return Typography.size[size] || Typography.size["small"];
};
const getLineHeight = () => {
if (typeof size === "number") return size * 1.5;
return Typography.lineHeight[size] || Typography.lineHeight["small"];
};
const getTextColor = () => {
if (dimRate)
return setOpacity(Colors.text, parseFloat(dimRate.replace("%", "")) / 100);
return Colors.text;
};
const getFontFamily = () => {
return Typography.fontFamily[weight];
};
return (
<DefaultText
style={[
{
fontSize: getFontSize(),
fontFamily: getFontFamily(),
color: getTextColor(),
textTransform,
lineHeight: getLineHeight(),
flexShrink: 1,
},
style,
]}
ref={ref}
{...otherProps}
>
{children}
</DefaultText>
);
});
Text.displayName = "Text";
🧪 Example Usage
<Text size="large" weight="bold" dimRate="80%">
Responsive, readable, and beautiful!
</Text>
Or if you want to use it manually:
<Text size={responsiveFontSize(18)}>
This font adjusts to your screen size.
</Text>
🗂 Bonus
Centralize Your Typography Config
To keep things clean and consistent, we’ve centralized font sizes, line heights, and font families inside a shared Typography config.
This config is already being used inside the custom component we built earlier — so all of your app's text elements can follow the same responsive rules without repeating logic.
export const Typography = {
size: {
small: responsiveFontSize(14),
medium: responsiveFontSize(16),
large: responsiveFontSize(20),
},
lineHeight: {
small: responsiveFontSize(21),
medium: responsiveFontSize(24),
large: responsiveFontSize(30),
},
fontFamily: {
regular: "Inter-Regular",
bold: "Inter-Bold",
},
};
export type FontWeight = keyof typeof Typography.headline.fontFamily;
export type TextSize = keyof typeof Typography.headline.size;
🎨 Built-in Dimming with setOpacity()
We also included a small utility to fine-tune the text color’s visibility — for subtle use cases like secondary labels or disabled states:
export const setOpacity = (hex: string, alpha: number) =>
`${hex}${Math.floor(alpha * 255)
.toString(16)
.padStart(2, "0")}`;
This function is used in the dimRate prop of the component to apply alpha transparency directly to the color.
🧠 What it does:
It takes:
- A base hex color (e.g. #000000)
- An alpha value from 0 to 1
- And appends a two-digit hex representation of that alpha
🧪 Example:
setOpacity("#000000", 0.5); // → "#00000080" (50% transparent black)
setOpacity("#FF5733", 0.2); // → "#FF573333" (20% transparent orange)
This makes it super easy to implement things like:
<Text dimRate="60%">This text is slightly dimmed</Text>
🚀 Wrapping Up
Responsive typography isn’t a “nice-to-have”—it’s essential. With a little setup:
- Your text becomes readable across all devices,
- Your layouts break less,
- And your app just feels more polished.
📦 Bundle your responsiveFontSize()
and <Text />
component, and you’ve got a future-proof solution that your whole team can use.
🧠 TL;DR
Feature | Static Font | Responsive Font |
---|---|---|
Scales to screen size? | ❌ | ✅ |
Looks consistent across devices? | ❌ | ✅ |
Respects design system? | 🧐 | ✅ |
Makes users happy? | 😐 | 😍 |
💬 Got questions?
Got questions about scaling fonts, making text more accessible, or handling dark mode like a pro? Let me know in the comments — happy to help you level up your UI game!
Top comments (1)
Nice one