{"id":"l-rn-1-3","courseId":"react-native-apps","lang":"en","kind":"lesson","summary":"Build Trailhead’s hike detail screen with View, Text, Image and ScrollView, and avoid the text and image rules that crash or blank out mobile screens.","html":"<p>On the web, you can throw a string into a <code dir=\"ltr\">div</code>, set a font on <code dir=\"ltr\">body</code>, and drop in an <code dir=\"ltr\">&lt;img&gt;</code> without thinking about its size. Try the same in React Native and you’ll get a red error screen, text in the wrong font, or an image that never appears. Four components carry most screens, and each has one rule that catches web developers.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">View: a box, nothing more</h2>\n<p><code dir=\"ltr\">View</code> is the building block for layout: a rectangle that can have a background, a border, padding and children. It maps to a native container view. It has no text node, so it <strong>cannot hold strings</strong>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">TSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// Throws: Text strings must be rendered within a &#x3C;Text> component.</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Ridge Loop&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// Works</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Ridge Loop&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>Think of <code dir=\"ltr\">View</code> as the <code dir=\"ltr\">div</code> you use for layout, minus everything a <code dir=\"ltr\">div</code> does with text. It’s also the thing you style most: cards, rows, dividers, badges and spacers are all Views with a background, a border radius or some padding. Views are cheap, but not free; each one becomes a real native view, so a list row with twelve nested wrappers costs more than one with four. Flatten when a wrapper adds nothing.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Text: the only place for strings</h2>\n<p>Every visible string goes inside <code dir=\"ltr\">&lt;Text&gt;</code>. Nested <code dir=\"ltr\">&lt;Text&gt;</code> elements become one paragraph, and that’s also the <strong>only</strong> place style inheritance exists: a child <code dir=\"ltr\">Text</code> inherits font, color and size from a parent <code dir=\"ltr\">Text</code>.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">TSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ fontSize: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">16</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, color: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#333'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  Ridge Loop is &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ fontWeight: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'700'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>8.4 km&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">> with 420 m of climbing.</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>A <code dir=\"ltr\">View</code> never passes text styles down. If you want a consistent font across the app, make a component (an <code dir=\"ltr\">AppText</code> that sets your defaults) rather than hunting for a global stylesheet that doesn’t exist.</p>\n<p>Text also scales with the user’s accessibility settings. Someone who has set a larger font size in iOS or Android settings gets bigger text in your app automatically, which is good, and which means a fixed-height box around text will eventually clip it. Leave room to grow; prefer padding over fixed heights for anything that contains words.</p>\n<p>Two props you’ll use constantly: <code dir=\"ltr\">numberOfLines={2}</code> truncates with an ellipsis (perfect for list cards), and <code dir=\"ltr\">selectable</code> lets users copy the text.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>The <code dir=\"ltr\">&amp;&amp;</code> that renders a zero</span></p>\n<p><code dir=\"ltr\">{hike.photoCount &amp;&amp; &lt;Text&gt;{hike.photoCount} photos&lt;/Text&gt;}</code> works on the web, where a stray <code dir=\"ltr\">0</code> shows up as a harmless character. In React Native, <code dir=\"ltr\">0 &amp;&amp; …</code> evaluates to <code dir=\"ltr\">0</code>, React tries to render that number directly inside a <code dir=\"ltr\">View</code>, and the app throws. Write a real boolean: <code dir=\"ltr\">{hike.photoCount &gt; 0 &amp;&amp; …}</code>.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">Image: tell it how big</h2>\n<p>For images that ship with the app, <code dir=\"ltr\">require()</code> resolves the file at build time, so React Native knows the image’s dimensions:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">TSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Image</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> source</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">require</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'../../assets/images/trail-placeholder.png'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> /></span></span></code></pre></div>\n<p>For images from the network, React Native can’t know the size until the download finishes, so you must give one. Without it the image renders at 0 by 0, silently.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">TSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Image</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  source</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ uri: hike.photoUrl }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ width: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'100%'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, height: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">220</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, borderRadius: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">12</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  resizeMode</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"cover\"</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  accessibilityLabel</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">`Photo from ${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">hike</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">name</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">/></span></span></code></pre></div>\n<p><code dir=\"ltr\">resizeMode</code> works like CSS <code dir=\"ltr\">object-fit</code>: <code dir=\"ltr\">cover</code> fills and crops, <code dir=\"ltr\">contain</code> fits inside. For production apps, many teams use <code dir=\"ltr\">expo-image</code> instead: it has the same basic shape, adds disk caching and placeholders, and uses <code dir=\"ltr\">contentFit</code> in place of <code dir=\"ltr\">resizeMode</code>.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">ScrollView: content that may not fit</h2>\n<p>Native views don’t scroll on overflow. Scrolling is its own component, <code dir=\"ltr\">ScrollView</code>, and it has two quirks.</p>\n<p>First, it needs a <strong>bounded height</strong>: it scrolls within whatever space its parent gives it, so the parent chain needs <code dir=\"ltr\">flex: 1</code> (you’ll see why in the flexbox lesson). Second, padding and alignment for the scrolled content go in <code dir=\"ltr\">contentContainerStyle</code>, not <code dir=\"ltr\">style</code>. <code dir=\"ltr\">style</code> sizes the scrolling window; <code dir=\"ltr\">contentContainerStyle</code> styles the content that slides inside it.</p>\n<p>Here is Trailhead’s detail screen, with sample data for now:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">src/app/hike.tsx</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { Image, ScrollView, Text, View } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'react-native'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> hike</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  name: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Ridge Loop'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  photoUrl: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'https://images.example.com/ridge-loop.jpg'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  distanceKm: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">8.4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  elevationM: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">420</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  notes: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Steep first kilometre, then a long ridge with views of the lake. Bring water: the spring at the saddle was dry.'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  photoCount: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">};</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> default</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> HikeScreen</span><span style=\"color:#953800;--shiki-dark:#FFA657\">() </span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">ScrollView</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ flex: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> contentContainerStyle</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ padding: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">16</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, gap: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">12</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Image</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">        source</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ uri: hike.photoUrl }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">        style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ width: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'100%'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, height: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">220</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, borderRadius: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">12</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">        accessibilityLabel</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">`Photo from ${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">hike</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">name</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      /></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ fontSize: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">28</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, fontWeight: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'700'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">hike.name</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ flexDirection: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'row'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, gap: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">16</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">hike.distanceKm</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> km&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">hike.elevationM</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> m up&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">      {</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">hike.photoCount </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">></span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> &#x26;&#x26;</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">hike.photoCount</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> photos&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> style</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{ fontSize: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">16</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, lineHeight: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">24</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">hike.notes</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">ScrollView</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  );</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>Look at what isn’t there: no <code dir=\"ltr\">div</code>, no class names, no stylesheet import, and no stray strings. Numbers like <code dir=\"ltr\">hike.distanceKm</code> are inside <code dir=\"ltr\">Text</code>, so they render fine.</p>\n<aside class=\"callout callout-note\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4\"/><path d=\"M12 8h.01\"/></svg><span><span class=\"callout-kind\">Note</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>What about a list of 300 hikes?</span></p>\n<p><code dir=\"ltr\">ScrollView</code> renders every child immediately, even those far off screen. For a screen of content that’s fine. For a data list that grows, it means slow startup and high memory. You’ll use <code dir=\"ltr\">FlatList</code> for that in section 2.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">The rest of the core set</h2>\n<p>You’ll meet the others as Trailhead needs them: <code dir=\"ltr\">Pressable</code> and <code dir=\"ltr\">TextInput</code> in the next lesson, <code dir=\"ltr\">FlatList</code> and <code dir=\"ltr\">SectionList</code> for lists, <code dir=\"ltr\">ActivityIndicator</code> for loading, <code dir=\"ltr\">Switch</code> for toggles, <code dir=\"ltr\">Modal</code> for overlays, and <code dir=\"ltr\">KeyboardAvoidingView</code> for forms. Each maps to a native component, so they look and behave like the platform by default.</p>\n<p>Next you’ll make Trailhead interactive: buttons that respond to touch and inputs that handle typing.</p>\n","toc":[{"id":"sec-1","text":"View: a box, nothing more","level":2},{"id":"sec-2","text":"Text: the only place for strings","level":2},{"id":"sec-3","text":"Image: tell it how big","level":2},{"id":"sec-4","text":"ScrollView: content that may not fit","level":2},{"id":"sec-5","text":"The rest of the core set","level":2}],"takeaways":["<code dir=\"ltr\">View</code> is a layout box and can’t hold raw text; every string must be inside a <code dir=\"ltr\">&lt;Text&gt;</code>.","Text styles inherit only from a parent <code dir=\"ltr\">&lt;Text&gt;</code>, never from a <code dir=\"ltr\">&lt;View&gt;</code>.","Network images need an explicit width and height; local images from <code dir=\"ltr\">require()</code> know their own size.","<code dir=\"ltr\">ScrollView</code> renders all of its children at once and needs a bounded height, so use it for screens of content, not long data lists.","<code dir=\"ltr\">{count &amp;&amp; &lt;Text&gt;…&lt;/Text&gt;}</code> crashes when <code dir=\"ltr\">count</code> is 0, because 0 is rendered as a raw string; compare explicitly instead."],"further":[{"title":"Core Components and APIs","url":"https://reactnative.dev/docs/components-and-apis"},{"title":"Text","url":"https://reactnative.dev/docs/text"},{"title":"Image","url":"https://reactnative.dev/docs/image"}],"quiz":[{"q":"<p>Which line throws “Text strings must be rendered within a <Text> component”?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"tsx\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">TSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Ridge Loop&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  {</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">hike.photoCount </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">&#x26;&#x26;</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">hike.photoCount</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> photos&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">View</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n","options":[{"text":"Line 2, because <code dir=\"ltr\">Text</code> must be wrapped in another <code dir=\"ltr\">Text</code>.","why":"A <code dir=\"ltr\">Text</code> directly inside a <code dir=\"ltr\">View</code> is the normal case. Only raw strings need wrapping."},{"text":"Line 3, when <code dir=\"ltr\">photoCount</code> is 0, because <code dir=\"ltr\">0 &amp;&amp; …</code> evaluates to <code dir=\"ltr\">0</code> and React renders it as text inside a View.","why":"Correct. <code dir=\"ltr\">false</code>, <code dir=\"ltr\">null</code> and <code dir=\"ltr\">undefined</code> render nothing, but the number 0 renders as “0”. Use <code dir=\"ltr\">photoCount &gt; 0 &amp;&amp; …</code>."},{"text":"Neither; React Native silently ignores stray strings.","why":"It doesn’t. On the web a stray string becomes a text node, but a native View has no text node to put it in, so React Native throws."}],"answer":1},{"q":"<p>You set <code dir=\"ltr\">style={{ color: '#2f6f4e', fontSize: 18 }}</code> on a <code dir=\"ltr\">View</code>. What happens to the <code dir=\"ltr\">Text</code> inside it?</p>","options":[{"text":"It turns green at 18 points, like CSS inheritance.","why":"There’s no cascade from View to Text. Text only inherits from an ancestor Text."},{"text":"The app crashes because View doesn’t accept those keys.","why":"You’ll get a type error in TypeScript, but at runtime the View ignores those keys. The real problem is that nothing happens."},{"text":"Nothing changes; the Text keeps its default color and size.","why":"Correct. Put text styles on the Text itself, or build a small <code dir=\"ltr\">&lt;AppText&gt;</code> component that applies them."}],"answer":2},{"q":"<p><code dir=\"ltr\">&lt;Image source={{ uri: hike.photoUrl }} /&gt;</code> shows nothing, and there are no errors. What’s the most likely fix?</p>","options":[{"text":"Give it a size, for example <code dir=\"ltr\">style={{ width: '100%', height: 220 }}</code>.","why":"Correct. React Native can’t know a network image’s size before it downloads, so without a size the image is 0 by 0."},{"text":"Wrap the URL in <code dir=\"ltr\">require()</code>.","why":"<code dir=\"ltr\">require()</code> is for images bundled with the app, resolved at build time. It can’t load a URL."},{"text":"Use <code dir=\"ltr\">src</code> instead of <code dir=\"ltr\">source</code>.","why":"<code dir=\"ltr\">src</code> is the HTML attribute. React Native’s Image uses <code dir=\"ltr\">source</code>."}],"answer":0},{"q":"<p>Trailhead’s detail screen has a photo, a title, stats and a long description that may not fit. Which container fits best?</p>","options":[{"text":"A <code dir=\"ltr\">View</code> with <code dir=\"ltr\">overflow: 'scroll'</code>.","why":"Views never scroll, whatever their overflow style. Scrolling is a separate native component."},{"text":"A <code dir=\"ltr\">ScrollView</code>, because it’s a fixed screen of mixed content.","why":"Correct. A screen-sized ScrollView with a handful of children is exactly its job."},{"text":"A <code dir=\"ltr\">FlatList</code> with one item per paragraph.","why":"FlatList is for long, uniform data lists. For one screen of content it adds complexity and buys nothing."}],"answer":1}],"exercise":{"kind":"spot-bug","language":"tsx","code":"import { Image, ScrollView, Text, View } from 'react-native';\n\nexport function HikeSummary({ hike }) {\n  return (\n    <ScrollView contentContainerStyle={{ padding: 16, gap: 12 }}>\n      <Image\n        source={{ uri: hike.photoUrl }}\n        style={{ width: '100%', height: 200 }}\n      />\n      <Text style={{ fontSize: 24, fontWeight: '700' }}>{hike.name}</Text>\n      <View style={{ flexDirection: 'row', gap: 12 }}>\n        <Text>{hike.distanceKm} km</Text>\n        {hike.elevationM} m up\n      </View>\n      {hike.photoCount && <Text>{hike.photoCount} photos</Text>}\n      <Text numberOfLines={3}>{hike.notes}</Text>\n    </ScrollView>\n  );\n}","bugLines":[13,15],"title":"Find the two crashes in the hike summary","prompt":"<p>This <code dir=\"ltr\">HikeSummary</code> component is shown for every hike in Trailhead. It works for some hikes and shows the red error screen for others, always with <strong>Text strings must be rendered within a <Text> component</strong>. Select the <strong>two</strong> lines that can cause that error.</p>\n","hints":["Look for anything that could end up as a raw string or number directly inside a <code dir=\"ltr\">View</code> or <code dir=\"ltr\">ScrollView</code>.","One problem happens for every hike; the other only for hikes with a particular value.","What does <code dir=\"ltr\">0 &amp;&amp; anything</code> evaluate to?"],"explanation":"<p>Line 13 puts <code dir=\"ltr\">{hike.elevationM} m up</code> directly inside a <code dir=\"ltr\">View</code>, so it fails for every hike: wrap it as <code dir=\"ltr\">&lt;Text&gt;{hike.elevationM} m up&lt;/Text&gt;</code>. Line 15 fails only for hikes with no photos: <code dir=\"ltr\">0 &amp;&amp; …</code> evaluates to <code dir=\"ltr\">0</code>, which React tries to render as text inside the ScrollView. Use a real boolean: <code dir=\"ltr\">{hike.photoCount &gt; 0 &amp;&amp; &lt;Text&gt;{hike.photoCount} photos&lt;/Text&gt;}</code>. The network <code dir=\"ltr\">Image</code> is fine because it has an explicit width and height.</p>\n","checkLabels":{}},"words":720,"readMinutes":3}