skip to content

Dynamic Type Sizes

Text grows with the system font size unless allowFontScaling or maxFontSizeMultiplier stop it, and fixed-height rows clip at the largest sizes. Interviewers ask how you check a screen at those sizes.

part ofReact Nativeoverview, primer and where to startread it →
on this pageshow

explore

questions

5

In React Native, how does Text react to the system font size setting, and what do allowFontScaling and maxFontSizeMultiplier change?

level: juniorimportance: must knowfreq 48%

answer

  1. Text scales unless told otherwise
  2. allowFontScaling defaults to true
  3. maxFontSizeMultiplier caps the growth
  4. undefined inherits, 0 means no cap
  5. cap the big display text, not body

basics

~10 s

Text scales with the user's system font size by default because allowFontScaling is true; maxFontSizeMultiplier caps how far one Text, and the Text nested in it, may grow, while allowFontScaling={false} switches scaling off entirely.

solid answer

~50 s

By default every `Text` and `TextInput` follows the user's text size setting, because `allowFontScaling` defaults to `true`: a `fontSize: 16` renders larger when the user picks larger text. `maxFontSizeMultiplier` caps that growth for a node: a value of 1 or more is the ceiling, `0` means no cap even if a parent set one, and leaving it undefined inherits from the parent `Text` or the global default of no cap. I leave body text uncapped, and cap only text that is already large, such as a 48-point gate number on a boarding pass, to around 1.5, so it grows a little without breaking the layout. `allowFontScaling={false}` is a last resort for text that is not really reading material, never a global fix for layouts that break at large sizes. On iOS, `dynamicTypeRamp` also chooses which Dynamic Type curve a `Text` follows.

code

tsx · 19 lines
tsx
import {StyleSheet, Text, View} from 'react-native';

export function GateBadge({gate, boardingTime}: {gate: string; boardingTime: string}) {
  return (
    <View style={styles.badge}>
      <Text style={styles.label}>Gate</Text>
      <Text style={styles.gate} maxFontSizeMultiplier={1.5}>
        {gate}
      </Text>
      <Text style={styles.label}>Boarding {boardingTime}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  badge: {padding: 16},
  label: {fontSize: 16},
  gate: {fontSize: 48, fontWeight: '700'},
});

go deeper

for a junior

Recall that Text and TextInput scale with the system setting by default, and that maxFontSizeMultiplier caps and allowFontScaling={false} disables scaling.

for a middle

Explain the undefined, 0 and 1-or-more values of maxFontSizeMultiplier, how the cap inherits through nested Text, and where iOS dynamicTypeRamp fits.

for a senior

Show how you choose which text to cap on a real screen, keep body text uncapped, and review code that disables scaling to hide layout bugs.

for a principal

Set a design-system rule for text scaling, such as which type styles may carry a cap and how large, so teams stop making ad-hoc exceptions.

## What happens by default Both iOS and Android let people choose a larger (or smaller) text size in system settings. React Native honours that choice automatically: a `Text` styled with `fontSize: 16` renders at 16 multiplied by the user's font scale. The switch that controls this is the **`allowFontScaling`** prop on `Text` and `TextInput`, and its default is **`true`**. Nothing needs to be enabled; a React Native app scales its text unless the code stops it. This is the behaviour you want for reading text. The people who choose larger text often cannot read the default size comfortably, so an app that ignores the setting is hard or impossible for them to use. ## The two props | Prop | Default | Effect | |---|---|---| | `allowFontScaling` | `true` | when `false`, the text ignores the user's setting entirely | | `maxFontSizeMultiplier` | `undefined` | caps the scale factor this node can reach | `maxFontSizeMultiplier` accepts three kinds of values: - **`undefined` or `null`**: inherit the cap from the parent `Text`, or from the global default, which is `0` (no cap). - **`0`**: no cap for this node, ignoring any cap a parent set. - **`>= 1`**: the scale factor can grow up to this value. With `fontSize: 20` and `maxFontSizeMultiplier={2}`, the text never exceeds 40, however large the user's setting. Because the value inherits through nested `Text`, a cap on an outer `Text` applies to inner spans unless they override it. ## Choosing where to cap A cap is a compromise between honouring the user's setting and keeping a layout usable. On a boarding pass: 1. **Body and data text** (passenger name, flight, seat, boarding time): **no cap**. This is what the user needs to read. 2. **Display text that is already large** (a 48-point gate number): a **modest cap** such as `1.5`. It is legible at its default size, and tripling it would push everything else off screen. 3. **Text in fixed chrome** that genuinely cannot grow (a small tab label): cap it, and on iOS pair it with the large content viewer so people can still read it on a long press. `allowFontScaling={false}` removes the user's choice completely. It is occasionally right for text that is not reading material, such as characters drawn as a logo. It is never the right fix for a layout that clips at large sizes; that layout needs to be fixed instead. ## iOS: Dynamic Type ramps On iOS the system defines several **Dynamic Type** text styles, each with its own growth curve. React Native's iOS-only **`dynamicTypeRamp`** prop picks which curve a `Text` follows; the default is `'body'`. Values include `'caption1'`, `'footnote'`, `'headline'`, `'title1'` and `'largeTitle'`. Headings that are already big can follow a heading ramp, which grows differently from body text, rather than being capped by hand. ## Common mistakes - Setting `allowFontScaling={false}` on every `Text` to stop a layout from breaking. - Capping body text at `1` or `1.2`, which makes the setting almost useless for the people who need it most. - Forgetting `TextInput`: it has its own `allowFontScaling` and `maxFontSizeMultiplier`, so a form field can end up smaller than its label. - Testing only at the default size; the props only show their effect at large settings.

  • A parent Text sets maxFontSizeMultiplier={1.2}. How does a nested Text escape that cap?
    A nested `Text` with no value inherits the parent's cap. Setting `maxFontSizeMultiplier={0}` on the nested `Text` means no cap for that node, ignoring the parent and the global default, while a value of 1 or more sets its own ceiling.
  • Why is allowFontScaling={false} on every Text a poor fix for a layout that breaks at large sizes?
    It silences the user's setting for all text, so the people who chose larger text, often because they cannot read the default, lose it everywhere. The layout problem stays hidden rather than fixed. Fix the layout with wrapping, flexible heights and scrolling, and cap only the few pieces of large display text.

saying these in an interview costs you the question

  • React Native text ignores the system font size unless allowFontScaling is set to true.
  • maxFontSizeMultiplier={0} stops the text from scaling at all.
  • Capping all text at 1.2 is a reasonable accessibility compromise.
  • allowFontScaling={false} app-wide is the standard fix for large-text layout bugs.
  • TextInput always follows Text's scaling settings automatically.
open as a page

A React Native boarding-pass screen clips and overlaps at the largest system text size; how do you find and fix the causes?

level: seniorimportance: must knowfreq 40%

basics

~20 s

Reproduce it on devices at the largest text setting, then remove the fixed constraints: minHeight instead of height, no truncation of essential data, rows that wrap or stack, a scrolling screen, and caps only on already-large display text.

open as a page

In React Native, what does adjustsFontSizeToFit do, and when is it acceptable for text that grows too large?

level: middleimportance: should knowfreq 25%

basics

~20 s

adjustsFontSizeToFit shrinks a Text until it fits its constraints, such as numberOfLines and the available width, down to minimumFontScale; because it undoes the user's larger text, it suits only short labels in fixed space, never body text.

open as a page

In React Native, how do you keep a boarding pass's buttons at least 44pt on iOS and 48dp on Android as text grows?

level: middleimportance: should knowfreq 30%

basics

~20 s

Give the pressable area minHeight and minWidth of at least 48, which React Native's density-independent units make at least 44pt on iOS and 48dp on Android, pad small icons, and never fix the height, so targets grow with their text.

open as a page

In React Native, what is the iOS large content viewer, and when would you enable it with accessibilityShowsLargeContentViewer?

level: middleimportance: nice to knowfreq 12%

basics

~20 s

The iOS large content viewer shows an enlarged copy of an element's content on long press; you enable it with accessibilityShowsLargeContentViewer, optionally titled by accessibilityLargeContentTitle, for controls such as tab bar items whose text you had to cap.

open as a page