Version1

View та Text — фундаментальні будівельні блоки

View як контейнер для всього, Text як єдиний спосіб відображення тексту — глибоке розуміння двох найважливіших компонентів React Native, їх відмінностей від HTML та правил використання.

View та Text — фундаментальні будівельні блоки

Чому все починається з View та Text

Якщо ви прийшли з веб-розробки, ви звикли до багатого набору HTML-елементів: <div>, <span>, <p>, <h1>, <section>, <article>, <header>, <footer>. Кожен з них має семантичне значення, стандартизовані стилі за замовчуванням та специфічну поведінку. Це роками відшліфовані інструменти для створення документо-орієнтованих інтерфейсів.

У React Native цього різноманіття немає. Замість десятків тегів є два первинних будівельних блоки:

  • <View> — універсальний контейнер для будь-якого візуального елемента
  • <Text> — єдиний компонент для відображення тексту

Це може здаватися обмеженням, але насправді це — свобода. Замість того, щоб запам'ятовувати семантику десятків тегів, ви будуєте все з двох примітивів, явно визначаючи поведінку через стилі та пропси. Це компонентно-орієнтований підхід у чистому вигляді.

У цій статті ми детально розберемо, що таке View і Text, чим вони відрізняються від HTML-аналогів, які правила їх використання та які патерни з ними пов'язані. До кінця у вас буде повне розуміння фундаменту, на якому будується весь UI у React Native.


View — це <div>, але не зовсім

Що таке View у нативному світі

Коли ви пишете <View> у React Native, він не створює HTML-елемент. Натомість:

  • На iOS створюється екземпляр класу UIView з фреймворку UIKit
  • На Android — екземпляр android.view.View
  • На вебі (через react-native-web) — звичайний <div>

Це нативні платформенні об'єкти, які мають власну ієрархію, lifecycle, механізм малювання. React Nativebridge (а у New Architecture — JSI) передає інструкції з JavaScript у нативний код, де створюється реальний UI-елемент.

Loading diagram...
@startuml
skinparam style plain
skinparam defaultFontName "JetBrains Mono"
skinparam backgroundColor #f8fafc
skinparam defaultFontSize 13


title JSX → Нативні компоненти (платформо-залежні)

rectangle "JavaScript код" as js #3b82f6 {
  rectangle "<View>\n <Text>Привіт</Text>\n</View>" as jsx #2563eb
}

rectangle "React Native Bridge / JSI" as bridge #64748b

rectangle "iOS (UIKit)" as ios #f59e0b {
  rectangle "UIView" as uiview #d97706
  rectangle "UILabel: 'Привіт'" as uilabel #b45309
}

rectangle "Android (Views)" as android #22c55e {
  rectangle "android.view.View" as andview #16a34a
  rectangle "TextView: 'Привіт'" as textview #15803d
}

js -down-> bridge
bridge -down-> ios
bridge -down-> android

uiview -down-> uilabel : "дочірній"
andview -down-> textview : "дочірній"

note right of bridge
  Мост передає дерево компонентів
  та їх властивості у нативний код.
  Shadow Tree обчислює layout (Yoga).
end note

@enduml

View як контейнер: фундаментальне призначення

View — це контейнер. Він не має жодного візуального змісту сам по собі — це прозора коробка, яка:

  1. Групує дочірні елементи — організовує їх у візуальну ієрархію
  2. Визначає layout — через Flexbox-властивості (про це окрема стаття)
  3. Надає стилізацію — фон, рамки, тіні, заокруглення кутів
  4. Обробляє події — може реагувати на дотики через onPress (у поєднанні з Pressable)
  5. Керує accessibility — accessibility props для screen readers

Аналогія з будівництвом: якщо <Text> — це цегла з написом, то <View> — це каркас будівлі, що тримає ці цегли разом у потрібній формі.

Базовий приклад View

BasicView.tsx
import { View, StyleSheet } from 'react-native'

export default function BasicViewExample() {
    return (
        <View style={styles.container}>
            <View style={styles.box} />
        </View>
    )
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#f0f0f0',
        padding: 20,
    },
    box: {
        width: 100,
        height: 100,
        backgroundColor: '#4361ee',
        borderRadius: 12,
    },
})

У цьому прикладі:

  • Зовнішній <View> — це контейнер всього екрану (flex: 1 розтягує його на всю висоту)
  • Внутрішній <View> — це кольоровий квадрат з заокругленими кутами
  • Жодного тексту чи зображення — просто структура контейнерів
Ментальна модель:<View> у React Native = <div> у HTML + Flexbox за замовчуванням. Кожного разу, коли у вебі ви б написали <div>, у React Native пишіть <View>. Якщо потрібен семантичний елемент (<header>, <section>) — у RN це теж <View> + accessibility props.

Text — єдиний спосіб відобразити текст

Категоричне правило: текст тільки всередині <Text>

У HTML текст може існувати практично де завгодно:

<!-- HTML — все це валідно: -->
<div>Текст безпосередньо у div</div>
<p>Параграф</p>
<span>Інлайн-текст</span>
<button>Кнопка</button>

У React Native це неможливо. Будь-який текстовий контент повинен бути загорнутий у компонент <Text>. Спроба помістити текст безпосередньо у <View> викличе помилку:

// ❌ ПОМИЛКА — текст не може бути безпосередньо у View
<View>
  Цей текст викличе runtime error
</View>

// ✅ ПРАВИЛЬНО
<View>
  <Text>Цей текст відобразиться коректно</Text>
</View>

Чому така жорсткість? Тому що на нативних платформах текст — це окремий тип компонента з власними можливостями:

  • iOS: UILabel, UITextField, UITextView — спеціалізовані класи для тексту
  • Android: TextView, EditText — теж окремі класи

Текст не може «просто існувати» — він завжди повинен бути всередині текстового компонента. React Native переносить це обмеження у JavaScript-світ для консистентності.

Text як нативний компонент

Коли ви пишете <Text>, він перетворюється на:

  • UILabel на iOS
  • TextView на Android
  • <span> або <p> на вебі (react-native-web)

Це означає, що <Text> має:

  • Власні можливості стилізації (fontSize, fontWeight, color, lineHeight, textAlign)
  • Обробку перенесення рядків
  • Підтримку Unicode, емодзі, RTL-мов (справа наліво)
  • Accessibility для screen readers

Базовий приклад Text

BasicText.tsx
import { View, Text, StyleSheet } from 'react-native'

export default function BasicTextExample() {
    return (
        <View style={styles.container}>
            <Text style={styles.title}>Заголовок</Text>
            <Text style={styles.body}>
                Це звичайний текст. У React Native немає семантичних тегів як <code>&lt;h1&gt;</code>,{' '}
                <code>&lt;p&gt;</code>, <code>&lt;span&gt;</code> — є лише <code>&lt;Text&gt;</code>.
                Стилі визначають, як текст виглядає.
            </Text>
        </View>
    )
}

const styles = StyleSheet.create({
    container: {
        padding: 20,
    },
    title: {
        fontSize: 24,
        fontWeight: '700',
        color: '#1a1a2e',
        marginBottom: 12,
    },
    body: {
        fontSize: 16,
        color: '#4a4a4a',
        lineHeight: 24,
    },
})
Часта помилка новачків: спроба використати HTML-теги у тексті. <Text>Це <strong>жирний</strong> текст</Text>не працює. Замість цього використовуйте вкладені <Text> з власними стилями або бібліотеку для markdown/HTML-рендерингу.

Ключова відмінність від HTML: вкладеність та стилі

Вкладеність Text: успадкування стилів

На відміну від <View>, компоненти <Text> можуть бути вкладеними один в одного, і дочірні <Text> успадковують деякі стилі від батьківських. Це єдиний випадок «каскаду» у React Native:

NestedText.tsx
import { Text, StyleSheet } from 'react-native'

export default function NestedTextExample() {
    return (
        <Text style={styles.paragraph}>
            Це звичайний текст. <Text style={styles.bold}>Цей фрагмент жирний.</Text>{' '}
            <Text style={styles.colored}>Цей — кольоровий.</Text> А це знову звичайний.
        </Text>
    )
}

const styles = StyleSheet.create({
    paragraph: {
        fontSize: 16,
        color: '#333333',
        lineHeight: 24,
    },
    bold: {
        fontWeight: '700',
        // fontSize та color успадковуються від paragraph
    },
    colored: {
        color: '#4361ee',
        // fontSize успадковується
    },
})

Що успадковується у вкладених <Text>:

fontSize
number
Розмір шрифту. Якщо не вказаний у дочірньому — береться з батьківського.
fontFamily
string
Назва шрифту. Дочірній Text використовує той самий шрифт, якщо не перевизначений.
fontWeight
string
Вага шрифту ('400', '700', 'bold'). Дочірній може перевизначити.
fontStyle
'normal' | 'italic'
Стиль шрифту. Успадковується, але можна перевизначити.
color
string
Колір тексту. Успадковується від батьківського Text.
lineHeight
number
Міжрядковий інтервал. Успадковується.
textAlign
'left' | 'center' | 'right' | 'justify'
Вирівнювання тексту. Успадковується лише у вкладених Text.
letterSpacing
number
Відстань між літерами. Успадковується.
textDecorationLine
'none' | 'underline' | 'line-through'
Підкреслення чи закреслення. Успадковується.
Практичний патерн: створюйте базовий компонент <Typography> з пресетами стилів (Heading1, Heading2, Body, Caption) та використовуйте вкладені <Text> для акцентів (жирний, посилання, код). Це забезпечує консистентність дизайну без дублювання стилів.

Text не може містити View

Важливе обмеження: <Text> може містити тільки інші <Text> та рядкові літерали. Спроба вкласти <View> у <Text> викличе помилку:

// ❌ ПОМИЛКА — View не може бути всередині Text
<Text>
  Текст та
  <View style={{ width: 20, height: 20, backgroundColor: 'red' }} />
  квадрат
</Text>

// ✅ ПРАВИЛЬНО — Text всередині View
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
  <Text>Текст та </Text>
  <View style={{ width: 20, height: 20, backgroundColor: 'red' }} />
  <Text> квадрат</Text>
</View>

Це логічно: на нативному рівні UILabel (iOS) та TextView (Android) не можуть містити довільні view-компоненти всередині тексту. Вони створені для відображення лише тексту.


Порівняльна таблиця: View vs Text

АспектViewText
HTML-аналог<div><p>, <span>, <h1><h6>
Нативний компонент (iOS)UIViewUILabel
Нативний компонент (Android)android.view.ViewTextView
Може містити текст напряму❌ Ні (викличе помилку)✅ Так (єдиний спосіб)
Може містити інші View✅ Так❌ Ні
Може містити інші Text✅ Так (але текст має бути у <Text>)✅ Так (вкладеність)
Успадкування стилів❌ Ні (кожен View — незалежний)✅ Так (fontSize, color, font* properties)
Layout-можливості✅ Повний FlexboxОбмежений (тільки текстові властивості)
Фон, рамки, тіні✅ Так✅ Так (але рідко використовується)
Обробка дотиківЧерез <Pressable> або <TouchableOpacity>Через onPress prop (працює напряму)
accessibilityЧерез props (accessibilityRole)Через props + автоматично як текст

Layout-патерни з View

Оскільки <View> є основним контейнером, практично всі layout-рішення будуються через комбінацію View-компонентів зі стилями Flexbox. Ось найпоширеніші патерни:

Патерн 1: Вертикальний стек (за замовчуванням)

За замовчуванням flexDirection у React Native — 'column' (на відміну від веб-CSS, де за замовчуванням 'row'). Тому View автоматично розташовує дочірні елементи вертикально:

VerticalStack.tsx
import { View, Text, StyleSheet } from 'react-native'

export default function VerticalStack() {
    return (
        <View style={styles.container}>
            <View style={styles.box}>
                <Text>Елемент 1</Text>
            </View>
            <View style={styles.box}>
                <Text>Елемент 2</Text>
            </View>
            <View style={styles.box}>
                <Text>Елемент 3</Text>
            </View>
        </View>
    )
}

const styles = StyleSheet.create({
    container: {
        padding: 20,
        // flexDirection: 'column' — за замовчуванням, можна не писати
    },
    box: {
        backgroundColor: '#e0e0e0',
        padding: 16,
        marginBottom: 12,
        borderRadius: 8,
    },
})

Патерн 2: Горизонтальний ряд

Щоб розташувати елементи горизонтально, явно встановіть flexDirection: 'row':

HorizontalRow.tsx
import { View, Text, StyleSheet } from 'react-native'

export default function HorizontalRow() {
    return (
        <View style={styles.row}>
            <View style={styles.item}>
                <Text>1</Text>
            </View>
            <View style={styles.item}>
                <Text>2</Text>
            </View>
            <View style={styles.item}>
                <Text>3</Text>
            </View>
        </View>
    )
}

const styles = StyleSheet.create({
    row: {
        flexDirection: 'row',
        padding: 20,
        justifyContent: 'space-between', // рівномірний розподіл
    },
    item: {
        width: 80,
        height: 80,
        backgroundColor: '#4361ee',
        alignItems: 'center',
        justifyContent: 'center',
        borderRadius: 8,
    },
})
Flexbox у React Native — окрема стаття курсу, де детально розглядаються всі властивості (justifyContent, alignItems, flex, gap тощо). Тут ми лише показуємо базові приклади використання View для layout.

Патерн 3: Центрування вмісту

Одне з найчастіших завдань — відцентрувати елемент всередині контейнера:

CenteredContent.tsx
import { View, Text, StyleSheet } from 'react-native'

export default function CenteredContent() {
    return (
        <View style={styles.container}>
            <View style={styles.card}>
                <Text style={styles.text}>Я відцентрований</Text>
            </View>
        </View>
    )
}

const styles = StyleSheet.create({
    container: {
        flex: 1, // займає всю доступну висоту
        justifyContent: 'center', // вертикальне центрування
        alignItems: 'center', // горизонтальне центрування
        backgroundColor: '#f5f5f5',
    },
    card: {
        width: 200,
        padding: 24,
        backgroundColor: '#ffffff',
        borderRadius: 12,
        shadowColor: '#000',
        shadowOffset: { width: 0, height: 2 },
        shadowOpacity: 0.1,
        shadowRadius: 4,
        elevation: 3,
    },
    text: {
        fontSize: 16,
        textAlign: 'center',
        color: '#333',
    },
})
Швидкий спосіб центрування:justifyContent: 'center' + alignItems: 'center' на батьківському View. Це працює для будь-якого вмісту — тексту, зображень, кнопок.

Типова структура екрану: фіксована шапка, контент що прокручується, фіксований футер:

ScreenLayout.tsx
import { View, Text, StyleSheet, ScrollView } from 'react-native'

export default function ScreenLayout() {
    return (
        <View style={styles.screen}>
            {/* Header */}
            <View style={styles.header}>
                <Text style={styles.headerText}>Заголовок</Text>
            </View>

            {/* Content (прокручується) */}
            <ScrollView style={styles.content}>
                <Text style={styles.paragraph}>
                    Це контент, який може бути довгим і прокручуватись. Lorem ipsum dolor sit amet...
                </Text>
                {/* Багато контенту */}
            </ScrollView>

            {/* Footer */}
            <View style={styles.footer}>
                <Text style={styles.footerText}>Футер</Text>
            </View>
        </View>
    )
}

const styles = StyleSheet.create({
    screen: {
        flex: 1, // займає весь екран
    },
    header: {
        height: 60,
        backgroundColor: '#4361ee',
        justifyContent: 'center',
        alignItems: 'center',
    },
    headerText: {
        color: '#ffffff',
        fontSize: 18,
        fontWeight: '600',
    },
    content: {
        flex: 1, // займає весь простір між header та footer
        backgroundColor: '#ffffff',
    },
    paragraph: {
        padding: 20,
        fontSize: 16,
        lineHeight: 24,
    },
    footer: {
        height: 50,
        backgroundColor: '#f0f0f0',
        justifyContent: 'center',
        alignItems: 'center',
        borderTopWidth: 1,
        borderTopColor: '#e0e0e0',
    },
    footerText: {
        fontSize: 14,
        color: '#666',
    },
})
Loading diagram...
@startuml
skinparam style plain
skinparam defaultFontName "JetBrains Mono"
skinparam backgroundColor #f8fafc
skinparam defaultFontSize 13

title Типовий мобільний layout: Header-Content-Footer

rectangle "Екран (View: flex: 1)" as screen #f8f9fa {
  rectangle "Header (View: height: 60)" as header #4361ee {
    rectangle "<Text>Заголовок</Text>" as htxt #ffffff
  }
  
  rectangle "Content (ScrollView: flex: 1)" as content #ffffff {
    rectangle "<Text>Прокручуваний контент...</Text>" as ctxt #333333
    rectangle "... багато контенту ..." as more #e0e0e0
  }
  
  rectangle "Footer (View: height: 50)" as footer #f0f0f0 {
    rectangle "<Text>Футер</Text>" as ftxt #666666
  }
}

note right of header
  Фіксована висота, не прокручується
end note

note right of content
  flex: 1 — займає весь залишковий простір
  ScrollView дозволяє прокрутку
end note

note right of footer
  Фіксована висота, завжди видима
end note

@enduml

Живий приклад: Картка профілю

Об'єднаємо все вивчене у практичному прикладі — картка користувача з аватаром, ім'ям, описом та кнопкою:

TSXApp.tsx
iPhone
9:41

Loading…

react-native-web · not a real device

У цьому прикладі:

Структура View

Три вкладені рівні: container (фон екрану) → card (картка) → внутрішні блоки (аватар, текст, кнопка). Кожен View має чітке призначення.

Text стилі

name, role, bio — різні стилі для різних рівнів тексту. Немає <h1> чи <p> — лише <Text> з явними стилями.

Умовні стилі

[styles.button, following && styles.buttonFollowing] — масив стилів, де другий стиль застосовується лише при following === true.

Pressable взаємодія

Кнопка змінює свій вигляд та текст при натисканні через стан following. Це типовий мобільний патерн.

Поширені помилки та їх вирішення


Практика


Резюме

🔲 View — універсальний контейнер

  • Аналог <div> у HTML, але без семантичних варіантів
  • Перетворюється на UIView (iOS) та android.view.View (Android)
  • Використовується для структури, layout (Flexbox) та стилізації
  • Може містити інші View, Text, Image та будь-які компоненти
  • Не може містити текст напряму — тільки через <Text>

📝 Text — єдиний спосіб відобразити текст

  • Перетворюється на UILabel (iOS) та TextView (Android)
  • Категоричне правило: будь-який текст має бути у <Text>
  • Може містити інші <Text> (вкладеність) — успадкування стилів
  • Не може містити <View> — тільки текстові вузли та інші Text
  • Підтримує текстові стилі: fontSize, color, fontWeight, lineHeight, textAlign

🎨 Стилі та каскад

  • У View немає каскаду — кожен компонент має власні стилі
  • У вкладених Text є каскад — fontSize, color, fontFamily успадковуються
  • Використовуйте StyleSheet.create() для оптимізації та типізації
  • Масив стилів [style1, style2] — другий перекриває перший

📐 Layout-патерни

  • За замовчуванням: flexDirection: 'column' (вертикальний стек)
  • Горизонтальний ряд: flexDirection: 'row'
  • Центрування: justifyContent: 'center' + alignItems: 'center'
  • Header-Content-Footer: комбінація фіксованих висот та flex: 1

⚠️ Поширені помилки

  • Текст напряму у View → помилка
  • View всередині Text → помилка
  • Layout-властивості на Text (flex, margin) → не спрацює коректно
  • Використання HTML-тегів (<div>, <p>) → помилка
  • Забути width/height для Image з мережі → не відобразиться

🎯 Що далі

Наступна стаття — StyleSheet та стилізація. Детально розглянемо, які CSS-властивості доступні, як працює StyleSheet.create(), що таке ViewStyle, TextStyle, ImageStyle, та як організувати стилі у великому проєкті.
Copyright © 2026