Skip to main content

✍️ Text: Typography in Native

Displaying and styling text the React Native way

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Understand why Text is required and how it differs from web text
  • Apply typography styles like fontSize, fontWeight, and lineHeight
  • Use system fonts and load custom fonts with Expo
  • Control text overflow with numberOfLines and ellipsizeMode
  • Create inline styled text using nested Text components
  • Implement accessible text for screen readers

⏱️ Estimated Time: 25-35 minutes

📑 In This Lesson

Why Text is Required

In React Native, you cannot place raw text directly inside a View. Every piece of text must be wrapped in a Text component. This is one of the first "gotchas" web developers encounter.

⚠️ This Will Crash Your App

// ❌ WRONG - Raw text in View causes a crash
<View>
  Hello World
</View>

// Error: Text strings must be rendered within a <Text> component

The correct approach:

// ✅ CORRECT - Text wrapped in Text component
<View>
  <Text>Hello World</Text>
</View>

Why This Requirement Exists

This isn't arbitrary — it reflects how native platforms work:

📱 iOS (UIKit)

UIView cannot display text directly. You need a UILabel or UITextView — specialized views designed for text rendering.

🤖 Android

android.view.View has no text rendering. You need a TextView — a specific component for displaying text.

React Native's Text component maps to these native text views, which is why it's required. The web's ability to put text anywhere is actually the exception, not the rule.

Text Component Maps to Native <Text> UILabel (iOS) TextView (Android)

React Native's Text bridges to each platform's native text component

📖 Key Insight

Think of Text as the only way to render text in React Native. Just as you need View for containers, you need Text for any string that appears on screen.

Typography: Web vs Native

If you're used to CSS typography, you'll find some familiar concepts and some surprising differences.

Key Differences

Aspect Web CSS React Native
Style inheritance Text styles cascade down the DOM ❌ No inheritance — each Text needs its own styles
Units px, em, rem, %, vw, vh Unitless numbers (density-independent pixels)
Font loading @font-face, Google Fonts expo-font or native linking
Default font Browser default (usually Times) System font (San Francisco on iOS, Roboto on Android)
line-height Unitless multiplier or length Absolute number only (like px)
letter-spacing em or px Absolute number only
text-decoration underline, line-through, etc. textDecorationLine, textDecorationStyle, textDecorationColor

The No-Inheritance Surprise

This is probably the biggest mental shift. On the web:

<!-- Web: Styles cascade down -->
<div style="font-family: Arial; color: blue;">
  <p>This is blue Arial</p>
  <p>So is this!</p>
</div>

In React Native, this does NOT work the same way:

// ❌ WRONG EXPECTATION - Styles don't cascade
<View style={{ fontFamily: 'Arial' }}>  {/* View can't even have fontFamily! */}
  <Text>This won't inherit anything</Text>
</View>

// ✅ CORRECT - Apply styles to each Text
<View>
  <Text style={styles.bodyText}>Styled text</Text>
  <Text style={styles.bodyText}>Also styled</Text>
</View>

✅ Exception: Nested Text

The ONE place styles do inherit is within nested Text components. A child Text will inherit styles from its parent Text:

<Text style={{ fontSize: 16, color: 'blue' }}>
  This is blue.
  <Text style={{ fontWeight: 'bold' }}>This is blue AND bold.</Text>
</Text>

We'll explore this powerful pattern in the Nested Text section.

Density-Independent Pixels

React Native uses unitless numbers that represent density-independent pixels (dp). The framework automatically scales these based on screen density:

// This is NOT 16 physical pixels
// It's 16 density-independent pixels
<Text style={{ fontSize: 16 }}>Hello</Text>

On a high-density screen (like iPhone 14 Pro), 16dp might render as 48 physical pixels. On a lower-density screen, it might be 32 physical pixels. The text appears the same physical size on both devices.

fontSize: 16 on Different Devices Hello 1x density 16 physical px Hello 2x density 32 physical px Hello 3x density 48 physical px Same perceived size on all devices ✓

Density-independent pixels ensure consistent text size across devices

Styling Text

Text accepts a rich set of style properties. Let's explore the most important ones.

Core Typography Styles

import { Text, StyleSheet } from 'react-native';

function TypographyDemo() {
  return (
    <>
      <Text style={styles.heading}>Main Heading</Text>
      <Text style={styles.subheading}>Subheading Text</Text>
      <Text style={styles.body}>Body text with normal weight and size.</Text>
      <Text style={styles.caption}>Small caption text</Text>
    </>
  );
}

const styles = StyleSheet.create({
  heading: {
    fontSize: 28,
    fontWeight: 'bold',      // 'normal', 'bold', '100'-'900'
    color: '#1a1a2e',
    letterSpacing: 0.5,      // Space between characters
    marginBottom: 8,
  },
  subheading: {
    fontSize: 20,
    fontWeight: '600',       // Semi-bold
    color: '#444',
    marginBottom: 12,
  },
  body: {
    fontSize: 16,
    fontWeight: 'normal',
    color: '#333',
    lineHeight: 24,          // Absolute value, not multiplier
    marginBottom: 8,
  },
  caption: {
    fontSize: 12,
    fontWeight: '300',       // Light
    color: '#888',
    fontStyle: 'italic',
  },
});

Complete Style Reference

Property Values Notes
fontSize number Size in density-independent pixels
fontWeight 'normal', 'bold', '100'-'900' String values only (not numbers)
fontStyle 'normal', 'italic' No 'oblique' like CSS
fontFamily string Font name (must be loaded)
color string Any valid color format
lineHeight number Absolute value (not multiplier)
letterSpacing number Space between characters
textAlign 'auto', 'left', 'right', 'center', 'justify' Horizontal alignment
textTransform 'none', 'uppercase', 'lowercase', 'capitalize' Case transformation
textDecorationLine 'none', 'underline', 'line-through', 'underline line-through' Text decoration
textDecorationStyle 'solid', 'double', 'dotted', 'dashed' iOS only
textDecorationColor string iOS only
textShadowColor string Shadow color
textShadowOffset { width: number, height: number } Shadow offset
textShadowRadius number Shadow blur radius

fontWeight Gotcha

⚠️ fontWeight Must Be a String

// ❌ WRONG - Number value
fontWeight: 600

// ✅ CORRECT - String value
fontWeight: '600'

Unlike CSS where both work, React Native requires string values for fontWeight.

lineHeight Behavior

On the web, you might use line-height: 1.5 as a multiplier. In React Native, lineHeight is an absolute value:

// Web CSS
line-height: 1.5;  // 1.5x the font size

// React Native - calculate it yourself
fontSize: 16,
lineHeight: 24,    // 16 * 1.5 = 24

💡 Pro Tip: Create a Helper

// Create text styles with proportional line height
const createTextStyle = (fontSize: number, lineHeightMultiplier = 1.5) => ({
  fontSize,
  lineHeight: fontSize * lineHeightMultiplier,
});

// Usage
const styles = StyleSheet.create({
  body: {
    ...createTextStyle(16, 1.5),  // fontSize: 16, lineHeight: 24
    color: '#333',
  },
});

Working with Fonts

React Native uses the system font by default — San Francisco on iOS and Roboto on Android. For custom fonts, you'll need to load them explicitly.

System Fonts

Without specifying a fontFamily, your text uses the platform's default:

// Uses San Francisco on iOS, Roboto on Android
<Text style={{ fontSize: 16 }}>System font text</Text>

You can also explicitly request platform-specific system fonts:

import { Platform } from 'react-native';

const styles = StyleSheet.create({
  text: {
    fontFamily: Platform.select({
      ios: 'Helvetica Neue',
      android: 'Roboto',
      default: 'System',
    }),
  },
});

Loading Custom Fonts with Expo

Expo makes loading custom fonts straightforward with the expo-font package and the useFonts hook:

import { Text, View } from 'react-native';
import { useFonts } from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
import { useEffect } from 'react';

// Prevent splash screen from hiding until fonts load
SplashScreen.preventAutoHideAsync();

export default function App() {
  const [fontsLoaded] = useFonts({
    'Poppins-Regular': require('./assets/fonts/Poppins-Regular.ttf'),
    'Poppins-Bold': require('./assets/fonts/Poppins-Bold.ttf'),
    'Poppins-Italic': require('./assets/fonts/Poppins-Italic.ttf'),
  });

  useEffect(() => {
    if (fontsLoaded) {
      SplashScreen.hideAsync();
    }
  }, [fontsLoaded]);

  if (!fontsLoaded) {
    return null;  // Or a loading indicator
  }

  return (
    <View style={{ flex: 1, padding: 20 }}>
      <Text style={{ fontFamily: 'Poppins-Bold', fontSize: 24 }}>
        Custom Font Heading
      </Text>
      <Text style={{ fontFamily: 'Poppins-Regular', fontSize: 16 }}>
        Body text with Poppins Regular
      </Text>
    </View>
  );
}

✅ Font Loading Best Practices

  • Load fonts at app startup, before rendering any text
  • Use the splash screen to hide the loading state
  • Keep font files in an assets/fonts folder
  • Use descriptive names that include weight/style (e.g., "Poppins-Bold")

Using Google Fonts with Expo

Expo provides pre-packaged Google Fonts that are even easier to use:

# Install a Google Font package
npx expo install @expo-google-fonts/inter expo-font
import { Text, View } from 'react-native';
import { useFonts, Inter_400Regular, Inter_700Bold } from '@expo-google-fonts/inter';
import * as SplashScreen from 'expo-splash-screen';
import { useEffect } from 'react';

SplashScreen.preventAutoHideAsync();

export default function App() {
  const [fontsLoaded] = useFonts({
    Inter_400Regular,
    Inter_700Bold,
  });

  useEffect(() => {
    if (fontsLoaded) {
      SplashScreen.hideAsync();
    }
  }, [fontsLoaded]);

  if (!fontsLoaded) return null;

  return (
    <View style={{ flex: 1, padding: 20 }}>
      <Text style={{ fontFamily: 'Inter_700Bold', fontSize: 24 }}>
        Inter Bold Heading
      </Text>
      <Text style={{ fontFamily: 'Inter_400Regular', fontSize: 16 }}>
        Inter Regular body text
      </Text>
    </View>
  );
}

Font Weight Variants

Unlike the web where you can use font-weight: 700 with any font, React Native requires separate font files for each weight:

Font Weights Require Separate Files Web CSS Approach font-family: 'Poppins'; font-weight: 400; // Regular font-weight: 700; // Bold ✓ One font-family, many weights React Native Approach fontFamily: 'Poppins-Regular' fontFamily: 'Poppins-Bold' fontFamily: 'Poppins-Light' ✓ Each weight = separate font file

Each font weight needs its own file and fontFamily name

Useful Text Props

Beyond styling, Text has several props that control behavior. These are essential for real-world apps.

numberOfLines and ellipsizeMode

Control how text truncates when it exceeds available space:

// Truncate to single line with ellipsis at end
<Text numberOfLines={1} ellipsizeMode="tail">
  This is a very long text that will be truncated...
</Text>

// Show first 3 lines only
<Text numberOfLines={3} ellipsizeMode="tail">
  {longArticlePreview}
</Text>

ellipsizeMode Options

Value Behavior Example
'tail' Truncate at end (default) "Hello Wor..."
'head' Truncate at beginning "...llo World"
'middle' Truncate in middle "Hel...orld"
'clip' Cut off without ellipsis "Hello Wor"

selectable

By default, users cannot select or copy text. Enable selection with:

// Allow user to select and copy this text
<Text selectable={true}>
  This text can be selected and copied
</Text>

// Common use case: copyable codes or IDs
<Text selectable style={styles.code}>
  Order #ABC-12345-XYZ
</Text>

onPress and onLongPress

Text can respond to touch events directly:

// Text as a link
<Text 
  style={styles.link}
  onPress={() => Linking.openURL('https://example.com')}
>
  Visit our website
</Text>

// Text with long press action
<Text
  onPress={() => console.log('Tapped')}
  onLongPress={() => console.log('Long pressed')}
>
  Tap or hold me
</Text>

💡 When to Use Text onPress vs Pressable

  • Text onPress: Simple inline links within paragraphs
  • Pressable: Buttons, cards, complex interactive elements with visual feedback

Text's onPress doesn't provide visual feedback by default — consider Pressable for better UX on buttons.

adjustsFontSizeToFit (iOS)

Automatically shrink text to fit within its container:

// Text shrinks to fit the container width
<Text 
  numberOfLines={1}
  adjustsFontSizeToFit
  minimumFontScale={0.5}  // Don't shrink below 50% of original size
  style={{ fontSize: 24 }}
>
  This heading will shrink if needed
</Text>

⚠️ Platform Note

adjustsFontSizeToFit works reliably on iOS. On Android, behavior can be inconsistent. For cross-platform solutions, consider calculating font size based on container width.

All Text Props Reference

<Text
  // Truncation
  numberOfLines={2}
  ellipsizeMode="tail"
  
  // Selection
  selectable={true}
  
  // Touch events
  onPress={() => {}}
  onLongPress={() => {}}
  onPressIn={() => {}}
  onPressOut={() => {}}
  
  // iOS specific
  adjustsFontSizeToFit={true}
  minimumFontScale={0.5}
  suppressHighlighting={false}  // Disable press highlight
  
  // Android specific
  android_hyphenationFrequency="normal"  // 'none', 'normal', 'full'
  textBreakStrategy="highQuality"  // 'simple', 'highQuality', 'balanced'
  
  // Layout
  onLayout={(event) => console.log(event.nativeEvent.layout)}
  onTextLayout={(event) => console.log(event.nativeEvent.lines)}
  
  // Accessibility (covered in depth later)
  accessible={true}
  accessibilityRole="text"
  accessibilityLabel="Description for screen readers"
  
  // Testing
  testID="my-text"
>
  Content here
</Text>

Nested Text for Inline Styles

One of Text's most powerful features is the ability to nest Text components. This enables inline styling — applying different styles to parts of the same paragraph.

Basic Inline Styling

// Bold word within a sentence
<Text style={styles.paragraph}>
  This is a <Text style={styles.bold}>very important</Text> message.
</Text>

// Multiple inline styles
<Text style={styles.paragraph}>
  You can mix <Text style={styles.bold}>bold</Text>, 
  <Text style={styles.italic}>italic</Text>, and 
  <Text style={styles.highlight}>highlighted</Text> text.
</Text>

const styles = StyleSheet.create({
  paragraph: {
    fontSize: 16,
    color: '#333',
    lineHeight: 24,
  },
  bold: {
    fontWeight: 'bold',
  },
  italic: {
    fontStyle: 'italic',
  },
  highlight: {
    backgroundColor: '#ffeb3b',
    color: '#333',
  },
});

Style Inheritance in Nested Text

Child Text components inherit styles from their parent Text:

<Text style={{ fontSize: 16, color: 'blue' }}>
  This is blue.
  <Text style={{ fontWeight: 'bold' }}>
    This is blue AND bold.
  </Text>
  <Text style={{ color: 'red' }}>
    This overrides to red, keeps fontSize: 16.
  </Text>
</Text>
flowchart TD
    A["Parent Text
fontSize: 16, color: blue"] --> B["Child Text 1
fontWeight: bold
Inherits: fontSize: 16, color: blue"] A --> C["Child Text 2
color: red
Inherits: fontSize: 16
Overrides: color"] style A fill:#e3f2fd,stroke:#1976d2 style B fill:#e8f5e9,stroke:#4caf50 style C fill:#ffebee,stroke:#f44336

Nested Text inherits parent styles, but can override specific properties

Inline Links

A common pattern is creating tappable links within paragraphs:

import { Text, Linking, StyleSheet } from 'react-native';

function TermsText() {
  return (
    <Text style={styles.terms}>
      By signing up, you agree to our{' '}
      <Text 
        style={styles.link}
        onPress={() => Linking.openURL('https://example.com/terms')}
      >
        Terms of Service
      </Text>
      {' '}and{' '}
      <Text 
        style={styles.link}
        onPress={() => Linking.openURL('https://example.com/privacy')}
      >
        Privacy Policy
      </Text>
      .
    </Text>
  );
}

const styles = StyleSheet.create({
  terms: {
    fontSize: 14,
    color: '#666',
    textAlign: 'center',
  },
  link: {
    color: '#2196F3',
    textDecorationLine: 'underline',
  },
});

✅ Note the {' '} Spacers

When nesting Text components, whitespace handling can be tricky. Use {' '} to explicitly add spaces between inline elements. Otherwise, words might run together.

Rich Text Patterns

// Price with currency styling
function Price({ amount, currency = 'USD' }) {
  return (
    <Text style={styles.price}>
      <Text style={styles.currency}>{currency} </Text>
      <Text style={styles.amount}>{amount.toFixed(2)}</Text>
    </Text>
  );
}

// Username mention
function PostContent({ text, mentions }) {
  // Simplified - real implementation would parse text
  return (
    <Text style={styles.post}>
      Great meeting with <Text style={styles.mention}>@johndoe</Text> today!
    </Text>
  );
}

// Code inline
function DocText() {
  return (
    <Text style={styles.doc}>
      Use the <Text style={styles.code}>useState</Text> hook to manage state.
    </Text>
  );
}

Text Accessibility

Text is automatically accessible to screen readers, but you can enhance the experience with additional props.

Automatic Accessibility

By default, Text components are read by screen readers. The text content becomes the accessibility label:

// Screen reader announces: "Hello World"
<Text>Hello World</Text>

Custom Accessibility Labels

Sometimes the visible text isn't sufficient for screen reader users:

// Visible: "5 min" — Screen reader: "5 minutes ago"
<Text accessibilityLabel="5 minutes ago">5 min</Text>

// Visible: "$99" — Screen reader: "99 dollars"
<Text accessibilityLabel="99 dollars">$99</Text>

// Icon-only text needs a label
<Text accessibilityLabel="Favorite">❤️</Text>

Accessibility Roles for Text

// Heading for navigation
<Text 
  accessibilityRole="header"
  style={styles.heading}
>
  Shopping Cart
</Text>

// Link
<Text
  accessibilityRole="link"
  accessibilityHint="Opens in browser"
  onPress={() => Linking.openURL(url)}
>
  Learn more
</Text>

// Alert or important message
<Text accessibilityRole="alert">
  Payment failed. Please try again.
</Text>

Accessibility for Nested Text

When Text is nested, screen readers combine them into a single announcement by default:

// Screen reader announces: "Price 99 dollars 99 cents"
<Text>
  Price{' '}
  <Text style={styles.dollars}>$99</Text>
  <Text style={styles.cents}>.99</Text>
</Text>

// To make child text separately accessible:
<Text>
  Price{' '}
  <Text 
    accessible={true}
    accessibilityLabel="99 dollars and 99 cents"
  >
    $99.99
  </Text>
</Text>

Dynamic Content Announcements

For content that updates (like a counter), use accessibility live regions:

import { AccessibilityInfo } from 'react-native';

function Counter({ count }) {
  useEffect(() => {
    // Announce count changes to screen readers
    AccessibilityInfo.announceForAccessibility(`Count is now ${count}`);
  }, [count]);

  return (
    <Text 
      accessibilityLiveRegion="polite"
      accessibilityLabel={`Count: ${count}`}
    >
      {count}
    </Text>
  );
}

💡 Testing Accessibility

  • iOS: Enable VoiceOver in Settings → Accessibility → VoiceOver
  • Android: Enable TalkBack in Settings → Accessibility → TalkBack
  • Expo Go: Works with device accessibility features enabled

Test your app with these screen readers regularly — it's the only way to truly understand the experience.

Hands-On Exercises

Practice makes perfect! These exercises will help you master Text styling and behavior.

Exercise 1: Typography Scale

Goal: Create a typography scale component that demonstrates different text sizes and weights.

Requirements:

  • Display 5 text levels: H1 (32px bold), H2 (24px semibold), H3 (20px medium), Body (16px regular), Caption (12px light)
  • Each should have appropriate line height (1.3x for headings, 1.5x for body)
  • Use a consistent color scheme (dark for headings, gray for body, light gray for caption)
💡 Hint

Create a StyleSheet with separate styles for each level. Remember lineHeight must be an absolute number, not a multiplier.

✅ Solution
import { View, Text, StyleSheet } from 'react-native';

export default function TypographyScale() {
  return (
    <View style={styles.container}>
      <Text style={styles.h1}>Heading 1</Text>
      <Text style={styles.h2}>Heading 2</Text>
      <Text style={styles.h3}>Heading 3</Text>
      <Text style={styles.body}>
        Body text for paragraphs and general content. 
        This should be comfortable to read in longer passages.
      </Text>
      <Text style={styles.caption}>
        Caption text for labels, timestamps, and metadata
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#fff',
    gap: 16,
  },
  h1: {
    fontSize: 32,
    fontWeight: 'bold',
    color: '#1a1a2e',
    lineHeight: 42,  // 32 * 1.3
  },
  h2: {
    fontSize: 24,
    fontWeight: '600',
    color: '#1a1a2e',
    lineHeight: 31,  // 24 * 1.3
  },
  h3: {
    fontSize: 20,
    fontWeight: '500',
    color: '#333',
    lineHeight: 26,  // 20 * 1.3
  },
  body: {
    fontSize: 16,
    fontWeight: 'normal',
    color: '#555',
    lineHeight: 24,  // 16 * 1.5
  },
  caption: {
    fontSize: 12,
    fontWeight: '300',
    color: '#888',
    lineHeight: 18,  // 12 * 1.5
  },
});

Exercise 2: Truncated Card Preview

Goal: Create an article preview card with truncated text.

Requirements:

  • Title: max 2 lines, truncate with ellipsis at end
  • Preview text: max 3 lines, truncate with ellipsis
  • Author and date on a single line (use "•" as separator)
  • Card should have padding, rounded corners, and subtle shadow
💡 Hint

Use numberOfLines and ellipsizeMode="tail". The author/date line can be a View with flexDirection: 'row'.

✅ Solution
import { View, Text, StyleSheet } from 'react-native';

const article = {
  title: 'Understanding React Native Performance: A Deep Dive into Optimization Techniques',
  preview: 'Learn how to build blazing fast mobile apps with React Native. We cover memoization, list optimization, image caching, and more advanced techniques that will make your app feel native.',
  author: 'Jane Developer',
  date: 'Dec 27, 2024',
};

export default function ArticleCard() {
  return (
    <View style={styles.card}>
      <Text 
        style={styles.title}
        numberOfLines={2}
        ellipsizeMode="tail"
      >
        {article.title}
      </Text>
      
      <Text 
        style={styles.preview}
        numberOfLines={3}
        ellipsizeMode="tail"
      >
        {article.preview}
      </Text>
      
      <View style={styles.meta}>
        <Text style={styles.author}>{article.author}</Text>
        <Text style={styles.separator}> • </Text>
        <Text style={styles.date}>{article.date}</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  card: {
    backgroundColor: 'white',
    borderRadius: 12,
    padding: 16,
    margin: 16,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 8,
    elevation: 3,
  },
  title: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1a1a2e',
    marginBottom: 8,
    lineHeight: 24,
  },
  preview: {
    fontSize: 14,
    color: '#666',
    lineHeight: 21,
    marginBottom: 12,
  },
  meta: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  author: {
    fontSize: 12,
    color: '#2196F3',
    fontWeight: '500',
  },
  separator: {
    fontSize: 12,
    color: '#ccc',
  },
  date: {
    fontSize: 12,
    color: '#999',
  },
});

Exercise 3: Inline Rich Text

Goal: Create a paragraph with mixed inline styles, including a tappable link.

Requirements:

  • Normal paragraph text in dark gray
  • One word in bold
  • One phrase in italic
  • An inline code snippet with background color
  • A tappable link (underlined, blue) that logs to console when pressed
💡 Hint

Use nested Text components. For the code style, use backgroundColor and fontFamily: 'monospace' (or platform-specific monospace font).

✅ Solution
import { Text, StyleSheet, Platform } from 'react-native';

export default function RichParagraph() {
  return (
    <Text style={styles.paragraph}>
      React Native is a <Text style={styles.bold}>powerful</Text> framework 
      for building mobile apps. It uses{' '}
      <Text style={styles.italic}>native components</Text> under the hood, 
      which means your app feels truly native. To get started, run{' '}
      <Text style={styles.code}>npx create-expo-app</Text>{' '}
      in your terminal. For more info, check out the{' '}
      <Text 
        style={styles.link}
        onPress={() => console.log('Link pressed!')}
      >
        official documentation
      </Text>.
    </Text>
  );
}

const styles = StyleSheet.create({
  paragraph: {
    fontSize: 16,
    color: '#444',
    lineHeight: 26,
    padding: 20,
  },
  bold: {
    fontWeight: 'bold',
  },
  italic: {
    fontStyle: 'italic',
  },
  code: {
    fontFamily: Platform.select({
      ios: 'Menlo',
      android: 'monospace',
    }),
    backgroundColor: '#f0f0f0',
    paddingHorizontal: 4,
    fontSize: 14,
    color: '#d63384',
  },
  link: {
    color: '#2196F3',
    textDecorationLine: 'underline',
  },
});

Challenge: Selectable Quote Block

🏆 Bonus Challenge

Goal: Create a styled quote block with attribution that users can select and copy.

Features:

  • Large opening quotation mark (decorative)
  • Quote text in italic, slightly larger font
  • Attribution line with author name (bold) and source (regular)
  • Left border accent (like blockquote)
  • Selectable text for the quote and attribution
✅ Solution
import { View, Text, StyleSheet } from 'react-native';

export default function QuoteBlock() {
  return (
    <View style={styles.container}>
      <View style={styles.quoteBox}>
        <Text style={styles.quoteMark}>"</Text>
        <Text style={styles.quoteText} selectable>
          The only way to do great work is to love what you do. 
          If you haven't found it yet, keep looking. Don't settle.
        </Text>
        <Text style={styles.attribution} selectable>
          — <Text style={styles.author}>Steve Jobs</Text>, 
          Stanford Commencement Speech 2005
        </Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 20,
  },
  quoteBox: {
    backgroundColor: '#f8f9fa',
    borderLeftWidth: 4,
    borderLeftColor: '#667eea',
    paddingVertical: 20,
    paddingHorizontal: 24,
    borderRadius: 8,
  },
  quoteMark: {
    fontSize: 60,
    color: '#667eea',
    lineHeight: 60,
    marginBottom: -20,
    marginLeft: -8,
    opacity: 0.5,
  },
  quoteText: {
    fontSize: 18,
    fontStyle: 'italic',
    color: '#333',
    lineHeight: 28,
    marginBottom: 16,
  },
  attribution: {
    fontSize: 14,
    color: '#666',
  },
  author: {
    fontWeight: 'bold',
    color: '#444',
  },
});

Summary

🎉 Key Takeaways

  • Text is required for all text — you cannot put raw strings inside View
  • No style inheritance — except within nested Text components
  • Units are density-independent — just use numbers, no "px" suffix
  • lineHeight is absolute — calculate it from fontSize (e.g., 16 * 1.5 = 24)
  • fontWeight must be a string — use '600' not 600
  • Load custom fonts with expo-font — each weight needs a separate file
  • Use numberOfLines for truncation — combine with ellipsizeMode
  • Nest Text for inline styles — child Text inherits from parent Text
  • Add accessibility labels — especially for abbreviations and icons

Quick Reference

import { Text, StyleSheet, Platform } from 'react-native';

// Basic styled text
<Text style={styles.body}>Hello World</Text>

// Truncated text
<Text numberOfLines={2} ellipsizeMode="tail">Long text...</Text>

// Selectable text
<Text selectable>Copy me!</Text>

// Nested/inline styling
<Text style={styles.paragraph}>
  Normal text with <Text style={styles.bold}>bold</Text> word.
</Text>

// Tappable text
<Text onPress={() => handlePress()}>Tap me</Text>

const styles = StyleSheet.create({
  body: {
    fontSize: 16,
    fontWeight: 'normal',      // String, not number
    color: '#333',
    lineHeight: 24,            // Absolute, not multiplier
    letterSpacing: 0.5,
    textAlign: 'left',
  },
  bold: { fontWeight: 'bold' },
  paragraph: { fontSize: 16 },
});

🚀 What's Next?

Now that you've mastered Text, we'll explore the Image component — how to display images from local files, remote URLs, and handle different resizing modes.

✍️ Typography Mastered!

You now know how to style text like a pro in React Native. From basic styling to nested inline elements, you've got the tools to create beautiful, readable, and accessible text.