{"id":"l-rf-1-2","courseId":"react-fundamentals","lang":"en","kind":"lesson","summary":"Create a React 19 project with Vite, run the dev server, and trace how index.html, main.jsx and App.jsx connect so you know where every change goes.","html":"<p>A React project needs more than React. Browsers don’t understand JSX, your code is split into dozens of modules, and you want the page to update the moment you save a file. A build tool handles all of that. In 2026 the default choice for a client-side React app is <strong>Vite</strong>: it starts in well under a second, updates the browser on save, and produces a small optimized build when you’re ready to ship.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Create the project</h2>\n<p>Check your Node.js version first. Current Vite (7 and 8) needs Node 20.19+ or 22.12+; in practice, install a current LTS release (22 or 24), since Node 20 reached end of life in April 2026.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Terminal</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:#953800;--shiki-dark:#FFA657\">node</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -v</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\"># v24.x or v22.x: a current LTS release</span></span></code></pre></div>\n<p>Then scaffold the Watchlist project:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Terminal</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:#953800;--shiki-dark:#FFA657\">npm</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> create</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> vite@latest</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> watchlist</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --template</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> react</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">cd</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> watchlist</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">npm</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> install</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">npm</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> run</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> dev</span></span></code></pre></div>\n<p>The <code dir=\"ltr\">--</code> matters. It tells npm “everything after this belongs to the tool I’m running”, so <code dir=\"ltr\">--template react</code> reaches create-vite instead of being eaten by npm. <code dir=\"ltr\">@latest</code> always gives you the newest major version of Vite (Vite 8 at the time of writing); everything in this course works the same on Vite 7 or 8. Recent versions of create-vite may also offer to install dependencies and start the server for you; accepting is fine.</p>\n<p>The terminal prints a local URL, <code dir=\"ltr\">http://localhost:5173/</code>. Open it and you’ll see the Vite + React starter page with a counter button.</p>\n<aside class=\"callout callout-tip\" 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=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Picking a template</span></p>\n<p><code dir=\"ltr\">react</code> gives you plain JavaScript, which is what this course uses so you can focus on React itself. <code dir=\"ltr\">react-ts</code> is the same with TypeScript, and <code dir=\"ltr\">react-compiler</code> adds the optional React Compiler. You can move to TypeScript later without rewriting your components’ logic.</p>\n</aside>\n<h2 id=\"sec-2\" tabindex=\"-1\">How the pieces connect</h2>\n<p>Open the folder in your editor. Most files are configuration you won’t touch often. Three files matter right now, and they form a chain.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 250\" role=\"img\" aria-labelledby=\"l-rf-1-2-t1\"><defs><marker id=\"arrow-l-rf-1-2-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-rf-1-2-t1\">The browser loads index.html, which contains a root div and a module script pointing at src/main.jsx. main.jsx calls createRoot on the root div and renders App from src/App.jsx.</title>\n  <rect class=\"d-box\" x=\"20\" y=\"40\" width=\"190\" height=\"150\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"115\" y=\"70\" text-anchor=\"middle\">index.html</text>\n  <text class=\"d-code\" x=\"115\" y=\"110\" text-anchor=\"middle\">&lt;div id=\"l-rf-1-2-root\"&gt;</text>\n  <text class=\"d-code\" x=\"115\" y=\"140\" text-anchor=\"middle\">&lt;script type=</text>\n  <text class=\"d-code\" x=\"115\" y=\"160\" text-anchor=\"middle\">\"module\" src=…&gt;</text>\n  <rect class=\"d-box-primary\" x=\"250\" y=\"40\" width=\"190\" height=\"150\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"345\" y=\"70\" text-anchor=\"middle\">src/main.jsx</text>\n  <text class=\"d-code\" x=\"345\" y=\"110\" text-anchor=\"middle\">createRoot(root)</text>\n  <text class=\"d-code\" x=\"345\" y=\"140\" text-anchor=\"middle\">.render(&lt;App /&gt;)</text>\n  <rect class=\"d-box-accent\" x=\"480\" y=\"40\" width=\"180\" height=\"150\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"570\" y=\"70\" text-anchor=\"middle\">src/App.jsx</text>\n  <text class=\"d-code\" x=\"570\" y=\"110\" text-anchor=\"middle\">export default</text>\n  <text class=\"d-code\" x=\"570\" y=\"135\" text-anchor=\"middle\">function App()</text>\n  <path class=\"d-arrow\" d=\"M210 150 L248 150\" marker-end=\"url(#arrow-l-rf-1-2-1)\"/>\n  <path class=\"d-arrow\" d=\"M440 120 L478 120\" marker-end=\"url(#arrow-l-rf-1-2-1)\"/>\n  <text class=\"d-label-muted\" x=\"340\" y=\"230\" text-anchor=\"middle\">loads → mounts → renders</text>\n</svg><figcaption>From index.html to your App component</figcaption></figure>\n<p><strong>index.html</strong> sits at the project root, not in a <code dir=\"ltr\">public</code> folder. Vite treats it as the entry point. It contains an empty <code dir=\"ltr\">&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;</code> and one script tag: <code dir=\"ltr\">&lt;script type=&quot;module&quot; src=&quot;/src/main.jsx&quot;&gt;&lt;/script&gt;</code>.</p>\n<p><strong>src/main.jsx</strong> is where React takes over:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">src/main.jsx</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\"> { StrictMode } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'react'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { createRoot } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'react-dom/client'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> './index.css'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> App </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> './App.jsx'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">createRoot</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(document.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">getElementById</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'root'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">render</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\">StrictMode</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\">App</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\">StrictMode</span><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><code dir=\"ltr\">createRoot</code> takes a real DOM node and returns a root that React manages from then on. Everything inside <code dir=\"ltr\">#root</code> now belongs to React; don’t change it with <code dir=\"ltr\">document.querySelector</code> code of your own.</p>\n<p><strong>src/App.jsx</strong> is your first component and where you’ll spend most of your time. Replace its contents with something of your own to prove the chain works:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">src/App.jsx</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\">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\"> App</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\"> &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>My Watchlist&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h1</span><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>Save, and the browser updates without a reload. That’s hot module replacement (HMR) with React Fast Refresh, set up by the <code dir=\"ltr\">@vitejs/plugin-react</code> plugin in <code dir=\"ltr\">vite.config.js</code>. You can also delete <code dir=\"ltr\">src/App.css</code> and empty <code dir=\"ltr\">src/index.css</code>; you’ll style the app properly later.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">StrictMode, briefly</h2>\n<p><code dir=\"ltr\">&lt;StrictMode&gt;</code> adds extra checks in development. It renders each component twice and runs effects twice (mount, clean up, mount again) to flush out code that isn’t safe to repeat. If you see a log print twice in dev, this is why. It does nothing in production, so leave it on. It will matter when you reach effects in Section 3.</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>Opening index.html directly</span></p>\n<p>Double-clicking <code dir=\"ltr\">index.html</code> opens it with a <code dir=\"ltr\">file://</code> URL. Nothing compiles the JSX, the module fails to load, and you get a blank page. Always open the URL that <code dir=\"ltr\">npm run dev</code> prints.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">The four scripts</h2>\n<p><code dir=\"ltr\">package.json</code> defines the commands you’ll use:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Command</th>\n<th>What it does</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code dir=\"ltr\">npm run dev</code></td>\n<td>Starts the dev server with instant updates</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">npm run build</code></td>\n<td>Bundles and minifies the app into <code dir=\"ltr\">dist/</code></td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">npm run preview</code></td>\n<td>Serves <code dir=\"ltr\">dist/</code> locally to check the real build</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">npm run lint</code></td>\n<td>Runs the linter, including the Rules of Hooks</td>\n</tr>\n</tbody>\n</table></div><p>The rest of the folder is supporting cast. <code dir=\"ltr\">package.json</code> lists your dependencies (<code dir=\"ltr\">react</code>, <code dir=\"ltr\">react-dom</code>) and dev tools (<code dir=\"ltr\">vite</code>, <code dir=\"ltr\">@vitejs/plugin-react</code> and a linter). <code dir=\"ltr\">node_modules/</code> is where <code dir=\"ltr\">npm install</code> puts them; it’s large, it’s rebuilt from <code dir=\"ltr\">package.json</code> at any time, and it never goes into Git. <code dir=\"ltr\">public/</code> holds files served as-is at the site root, such as a favicon. The linter’s config file depends on the template version: current templates use Oxlint (<code dir=\"ltr\">.oxlintrc.json</code>), older ones and the ESLint option use <code dir=\"ltr\">eslint.config.js</code>.</p>\n<p><code dir=\"ltr\">dist/</code> is generated output. Never edit files in it, and don’t commit it to Git; the starter’s <code dir=\"ltr\">.gitignore</code> already excludes it. When you deploy in the last section, Vercel will run <code dir=\"ltr\">npm run build</code> for you.</p>\n<p>Your project is running and you know where React is mounted. If the dev server ever gets into a strange state after you install a package or rename files, stop it with Ctrl+C and run <code dir=\"ltr\">npm run dev</code> again. A restart takes a second and fixes more problems than it should. Next, you’ll write your first real component in <code dir=\"ltr\">App.jsx</code> and split the Watchlist into reusable pieces.</p>\n","toc":[{"id":"sec-1","text":"Create the project","level":2},{"id":"sec-2","text":"How the pieces connect","level":2},{"id":"sec-3","text":"StrictMode, briefly","level":2},{"id":"sec-4","text":"The four scripts","level":2}],"takeaways":["<code dir=\"ltr\">npm create vite@latest watchlist -- --template react</code> scaffolds a React project; <code dir=\"ltr\">npm run dev</code> serves it at localhost:5173 with hot updates.","The browser loads index.html, which loads src/main.jsx, which mounts the App component into the div with id root.","<code dir=\"ltr\">npm run build</code> writes an optimized static site to dist/, and <code dir=\"ltr\">npm run preview</code> serves that build locally.","StrictMode renders components twice and runs effects twice in development only, to surface bugs early."],"further":[{"title":"Getting Started (Vite)","url":"https://vite.dev/guide/"},{"title":"Build a React app from Scratch","url":"https://react.dev/learn/build-a-react-app-from-scratch"},{"title":"StrictMode reference","url":"https://react.dev/reference/react/StrictMode"}],"quiz":[{"q":"<p>You run <code dir=\"ltr\">npm create vite@latest watchlist --template react</code> with npm 10, and Vite asks you to pick a framework anyway. Why?</p>","options":[{"text":"The <code dir=\"ltr\">react</code> template was removed from recent versions of Vite.","why":"The <code dir=\"ltr\">react</code> template still exists, alongside <code dir=\"ltr\">react-ts</code>, <code dir=\"ltr\">react-compiler</code> and others."},{"text":"Vite only accepts templates when you are inside an existing project folder.","why":"The project name and template work together from any folder; Vite creates the new folder for you."},{"text":"npm 10 can’t run <code dir=\"ltr\">create</code> commands, so the flag is silently dropped.","why":"npm runs <code dir=\"ltr\">create</code> commands fine. It just treats unseparated flags as its own options."},{"text":"npm swallowed <code dir=\"ltr\">--template</code> as its own flag; you need <code dir=\"ltr\">--</code> before it so the flag reaches create-vite.","why":"Correct. With npm 7 and later, <code dir=\"ltr\">npm create vite@latest watchlist -- --template react</code> passes everything after <code dir=\"ltr\">--</code> to the scaffolding tool."}],"answer":3},{"q":"<p>Where does React actually attach your app to the page in a Vite project?</p>","options":[{"text":"In src/main.jsx, where <code dir=\"ltr\">createRoot(document.getElementById('root'))</code> renders <code dir=\"ltr\">&lt;App /&gt;</code>.","why":"Correct. main.jsx is the entry module; it finds the root div from index.html and hands it to React."},{"text":"In index.html, through a <code dir=\"ltr\">&lt;react-app&gt;</code> custom element.","why":"index.html only has a plain <code dir=\"ltr\">&lt;div id=&quot;root&quot;&gt;</code> and a module script tag. React itself is wired up in main.jsx."},{"text":"In vite.config.js, through the <code dir=\"ltr\">react()</code> plugin.","why":"The plugin teaches Vite to compile JSX and enables fast refresh. It doesn’t decide where your app renders."}],"answer":0},{"q":"<p>In development, a <code dir=\"ltr\">console.log</code> at the top of your App component prints twice on load. In the production build it prints once. What’s going on?</p>","options":[{"text":"Vite’s hot module replacement loads every module twice.","why":"HMR swaps changed modules after edits; it doesn’t run each component twice on the first load."},{"text":"There’s a bug in your component that makes it render in a loop.","why":"A render loop would print far more than twice and usually throws “Too many re-renders”."},{"text":"StrictMode deliberately renders components twice in development to expose impure rendering code.","why":"Correct. It’s a development-only check. The production build renders once."},{"text":"React 19 always renders every component twice before it shows the result.","why":"The double render only happens under StrictMode in development, not in production."}],"answer":2},{"q":"<p>You want to see exactly what your users will download before deploying. Which command sequence fits?</p>","options":[{"text":"Run <code dir=\"ltr\">npm run dev</code> and open the Network tab.","why":"The dev server serves unbundled source modules with dev-only tooling, which is not what ships."},{"text":"Run <code dir=\"ltr\">npm run build</code>, then <code dir=\"ltr\">npm run preview</code>.","why":"Correct. build writes the optimized site to dist/, and preview serves that folder locally so you can click through it."},{"text":"Open index.html directly from Finder or Explorer.","why":"Opening the file with file:// skips Vite entirely; the JSX would never be compiled and the page stays blank."}],"answer":1}],"exercise":{"kind":"order","items":["check-node","scaffold","enter-folder","install","start-dev","edit-app"],"title":"Set up the Watchlist project","prompt":"<p>You’re on a fresh laptop with Node.js installed. Put these steps in the order you’d run them to get the Watchlist project running and showing your own heading in the browser.</p>\n","hints":["You can’t install dependencies for a project that doesn’t exist yet.","npm commands run against the package.json in the current folder, so you need to be inside the project first."],"explanation":"<p>First confirm Node is new enough for Vite (20.19+ or 22.12+). Scaffolding creates the <code dir=\"ltr\">watchlist</code> folder, so you <code dir=\"ltr\">cd</code> into it before <code dir=\"ltr\">npm install</code>, which reads its <code dir=\"ltr\">package.json</code>. <code dir=\"ltr\">npm run dev</code> starts the server on port 5173. Only then does editing <code dir=\"ltr\">App.jsx</code> show up in the browser, via hot module replacement.</p>\n","checkLabels":{},"itemText":{"check-node":"<code dir=\"ltr\">node -v</code> — confirm Node.js 20.19+ or 22.12+","scaffold":"<code dir=\"ltr\">npm create vite@latest watchlist -- --template react</code>","enter-folder":"<code dir=\"ltr\">cd watchlist</code>","install":"<code dir=\"ltr\">npm install</code>","start-dev":"<code dir=\"ltr\">npm run dev</code>, then open http://localhost:5173","edit-app":"Replace src/App.jsx with your own <code dir=\"ltr\">&lt;h1&gt;My Watchlist&lt;/h1&gt;</code> and save"}},"words":774,"readMinutes":4}