CINXE.COM

Getting Started with React

<!DOCTYPE html><html><head><style type="text/css"> .cover-wrapper.astro-2xuBJN2O { width: 100%; height: 44vh; min-height: 20rem; max-height: 30rem; position: relative; background: #2a85ca40; overflow: hidden; } .cover.astro-2xuBJN2O,.cover-blur.astro-2xuBJN2O { position: absolute; top: 0; left: 0; bottom: 0; right: 0; height: 100%; width: 100%; } .cover-blur.astro-2xuBJN2O { -o-object-fit: cover; object-fit: cover; filter: blur(3px) brightness(1.5); transform: scale(1.1); } .cover.astro-2xuBJN2O { -o-object-fit: contain; object-fit: contain; filter: brightness(1.5); } @media (max-width: 1200px) { .cover-blur.astro-2xuBJN2O { -o-object-fit: cover; object-fit: cover; } .cover.astro-2xuBJN2O { -o-object-fit: cover; object-fit: cover; } } .layout.astro-2xuBJN2O { display: grid; grid-template-areas: "contents" "main"; grid-gap: 2rem; } @media (min-width: 800px) { .layout.astro-2xuBJN2O { grid-template-areas: "nav contents" "nav main"; } } @media (min-width: 1240px) { .layout.astro-2xuBJN2O { grid-template-areas: "nav main contents"; grid-template-columns: 16rem auto 20rem; } } .layout-nav.astro-2xuBJN2O { grid-area: nav; } @media (min-width: 800px) { .layout-nav.astro-2xuBJN2O { position: sticky; min-height: calc(100vh - 3.5rem); height: calc(100vh - 3.5rem); top: 3.5rem; } } .layout-main.astro-2xuBJN2O { grid-area: main; min-width: 0; padding: 24px 0 24px 0; } @media (min-width: 1240px) { .layout-contents.astro-2xuBJN2O { position: sticky; min-height: calc(100vh - 3.5rem); height: calc(100vh - 3.5rem); top: 3.5rem; } } h3 { position: relative; } .header-link { position: absolute; top: 0; right: 100%; height: 100%; display: flex; align-items: center; font-weight: 400; font-size: 0.75em; padding-left: 0.5rem; padding-right: 0.5rem; color: #c1c1c1; opacity: 0; transition: opacity 100ms linear, color 100ms linear; } .header-link:hover { color: #267dd6; } h3:hover .header-link { opacity: 1; }</style><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"><link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"><link rel="manifest" href="/favicon/site.webmanifest"><title>Getting Started with React</title><meta name="title" content="Getting Started with React"><meta name="description" content="Get started with this in-depth tutorial on how to build React applications and websites with Snowpack and developer tools like React Fast Refresh"><meta property="og:type" content="website"><meta property="og:url" content="TODO"><meta property="og:title" content="Getting Started with React"><meta property="og:description" content="Get started with this in-depth tutorial on how to build React applications and websites with Snowpack and developer tools like React Fast Refresh"><meta property="og:image" content="https://www.snowpack.dev/img/social-2.jpg"><meta property="twitter:card" content="summary_large_image"><meta property="twitter:url" content="TODO"><meta property="twitter:title" content="Getting Started with React"><meta property="twitter:description" content="Get started with this in-depth tutorial on how to build React applications and websites with Snowpack and developer tools like React Fast Refresh"><meta property="twitter:image" content="https://www.snowpack.dev/img/social-2.jpg"><link rel="stylesheet" href="/_astro/common-1ckxTT.css" type="text/css"><link href="https://fonts.googleapis.com/css2?family=Overpass:wght@400;700;900&amp;display=swap" rel="stylesheet"></head><body><nav class="nav astro-ahFEmudE"><button id="toc-drawer-button" class="mobile-open astro-ahFEmudE" type="button" aria-expanded="false" aria-controls="nav-primary"><svg focusable="false" class="hamburger astro-ahFEmudE" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><title>Toggle mobile menu</title><path d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z" class="astro-ahFEmudE"></path></svg></button><a class="logo astro-ahFEmudE" href="/"><svg class="logo-icon astro-ahFEmudE" viewBox="0 0 640 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="translate(-1.000000, 0.000000)" fill-rule="nonzero" class="astro-ahFEmudE"><path d="M635.92,462.7 L347.92,14.7 C342.03,5.54 331.89,0 321,0 C310.11,0 299.97,5.54 294.08,14.7 L6.08,462.7 C-0.250773249,472.547007 -0.699487627,485.064987 4.91,495.34 C10.522069,505.612419 21.2945349,512 33,512 L609,512 C620.71,512 631.48,505.61 637.09,495.33 C642.699457,485.058495 642.250708,472.543372 635.92,462.7 Z M321,91.18 L406.39,224 L321,224 L257,288 L218.94,249.94 L321,91.18 Z" id="Shape" class="astro-ahFEmudE"></path></g></svg><span class="logo-type astro-ahFEmudE">Snowpack</span></a><div class="search astro-ahFEmudE"><input type="text" name="search" placeholder="Search documentation..." class="search-input astro-ahFEmudE" id="search-form-input"><span class="search-hint astro-ahFEmudE"><span class="sr-only astro-ahFEmudE">Press </span><kbd class="font-sans astro-ahFEmudE"><abbr title="Command" style="text-decoration: none;" class="astro-ahFEmudE">⌘</abbr></kbd><span class="sr-only astro-ahFEmudE"> and </span><kbd class="font-sans astro-ahFEmudE">K</kbd><span class="sr-only astro-ahFEmudE"> to search</span></span></div><a href="https://github.com/FredKSchott/snowpack/releases" target="_blank" class="link version astro-ahFEmudE">v3.8.8</a><a href="https://github.com/FredKSchott/snowpack" target="_blank" class="link link__desktop astro-ahFEmudE"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" class="social astro-ahFEmudE" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" class="astro-ahFEmudE"></path></svg></a></nav><script> function handleMobileNav(evt) { evt.preventDefault(); /*If hidden-mobile class is enabled that means we are on desktop do overflow normal but we if we are at mobile fixed body position, so that its not scrollable(which currently causing bug) and navbar handling its owns scroll. Case to consider there are chance use can open navbar using toggle button and user when click on any link body postion should be unset */ document.body.classList.toggle('is-nav-open'); const isOpen = document.body.classList.contains('is-nav-open'); if (isOpen) { evt.target.setAttribute('aria-expanded', 'true'); } else { evt.target.setAttribute('aria-expanded', 'false'); } } const mobileNavBtn = document.getElementById('toc-drawer-button'); mobileNavBtn.addEventListener('click', handleMobileNav); mobileNavBtn.addEventListener('touchend', handleMobileNav); if (window.location.pathname.startsWith('/posts')) { mobileNavBtn.style.display = 'none'; } const searchFormInputEl = document.getElementById('search-form-input'); searchFormInputEl.addEventListener('keyup', () => { const gridTocEl = document.querySelector('#nav-primary'); if (searchFormInputEl.value) { gridTocEl.classList.add('is-mobile-hidden'); } else { gridTocEl.classList.remove('is-mobile-hidden'); } }); document.onkeydown = function (e) { if ((e.ctrlKey || e.metaKey) && e.which == 75) { e.preventDefault(); searchFormInputEl.focus(); } }; </script><script type="module" src="/_astro/src/components/docsearch.js"></script><doc-search api-key="562139304880b94536fc53f5d65c5c19" selector="#search-form-input" class="astro-ahFEmudE"></doc-search><div class="cover-wrapper astro-2xuBJN2O"><img class="cover-blur astro-2xuBJN2O" src="/img/ReactGuide.jpg" alt=""><img class="cover astro-2xuBJN2O" src="/img/ReactGuide.jpg" alt=""></div><div class="wrapper astro-2xuBJN2O"><section class="layout astro-2xuBJN2O"><aside id="nav-primary" class="layout-nav astro-2xuBJN2O"><nav class="nav astro-hk9uDi21"><ol class="contents astro-hk9uDi21"><li class="section astro-hk9uDi21"><span class="header astro-hk9uDi21">Concepts</span><ol class="items astro-hk9uDi21"><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/concepts/how-snowpack-works">How Snowpack Works</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/concepts/dev-server">The Dev Server</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/concepts/build-pipeline">The Build Pipeline</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/concepts/hot-module-replacement">Fast Refresh &amp; HMR</a></li></ol></li><li class="section astro-hk9uDi21"><span class="header astro-hk9uDi21">Getting Started</span><ol class="items astro-hk9uDi21"><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/tutorials/quick-start">Quick Start</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/tutorials/getting-started">Getting Started</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/tutorials/react">React</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/tutorials/svelte">Svelte</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/tutorials/vue">Vue</a></li></ol></li><li class="section astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/guides"><span class="header astro-hk9uDi21">Guides</span></a></li><li class="section astro-hk9uDi21"><span class="header astro-hk9uDi21">Reference</span><ol class="items astro-hk9uDi21"><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/reference/configuration">snowpack.config.js</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/reference/cli-command-line-interface">Command Line API</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/reference/javascript-interface">JavaScript API</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/reference/plugins">Plugin API</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/reference/environment-variables">Environment Variables</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/reference/hot-module-replacement">HMR API</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/reference/supported-files">Supported Files</a></li><li class="astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/reference/common-error-details">Common Errors</a></li></ol></li><li class="section astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/plugins"><span class="header astro-hk9uDi21">Plugin Catalog</span></a></li><li class="section astro-hk9uDi21"><a class="link astro-hk9uDi21" href="/news"><span class="header astro-hk9uDi21">Community &amp; News</span></a></li></ol></nav></aside><div class="astro-2xuBJN2O"><script type="module" defer="" src="/js/index.js"></script><aside class="subnav astro-6RrcgMjt"><div class="astro-6RrcgMjt"><h4 class="header astro-6RrcgMjt">On this page</h4><nav class="toc astro-6RrcgMjt"><ol class="astro-6RrcgMjt"><li class="astro-6RrcgMjt"><a href="#getting-started" class="astro-6RrcgMjt">Getting started</a></li><li class="astro-6RrcgMjt"><a href="#create-your-first-react-component" class="astro-6RrcgMjt">Create your first React component</a></li><li class="astro-6RrcgMjt"><a href="#customize-your-project-layout" class="astro-6RrcgMjt">Customize your project layout</a></li><li class="astro-6RrcgMjt"><a href="#styling-your-project" class="astro-6RrcgMjt">Styling your project</a></li><li class="astro-6RrcgMjt"><a href="#making-snowpack-even-faster-with-fast-refresh" class="astro-6RrcgMjt">Making Snowpack Even Faster with Fast Refresh</a></li><li class="astro-6RrcgMjt"><a href="#going-further" class="astro-6RrcgMjt">Going further</a></li></ol></nav><hr class="astro-6RrcgMjt"></div><h4 class="header astro-6RrcgMjt">Suggest a change</h4><a href="https://github.com/FredKSchott/snowpack/blob/main/docs" class="astro-6RrcgMjt">Edit this page on GitHub</a></aside></div><article class="layout-main astro-2xuBJN2O"><div class="content astro-2xuBJN2O"><h2 class="content-title astro-2xuBJN2O">Getting Started with React</h2><div class="content-layout astro-2xuBJN2O"><div class="content-body astro-2xuBJN2O"><p>Snowpack is a great fit for <a href="https://reactjs.org/">React</a> projects of any size. It’s easy to get started and can scale to projects containing thousands of components and pages without any impact on development speed. Unlike traditional React application tooling, Snowpack saves you from getting bogged down with complex bundler setups and configuration files.</p><p>In this guide, you’ll go from an empty directory to a fully configured Snowpack project with support for React and several other useful developer tools. In the process, you’ll learn:</p><ul><li>How to set up your Snowpack development environment</li><li>Adding your first React component</li><li>Working with CSS, images and other web assets</li><li>Enabling <a href="https://reactnative.dev/docs/fast-refresh">Fast Refresh</a> mode for React</li><li>Connecting your favorite tools</li></ul><p>Prerequisites: Snowpack is a command line tool installed from npm. This guide assumes a basic understanding of Node.js, npm, and how to run commands in the terminal. Knowledge of React is not required, Snowpack is a great way to learn React!</p><blockquote><p>💡 Tip: if you want to jump to the end to see a full featured React setup, the <a href="https://github.com/withastro/snowpack/tree/main/create-snowpack-app/app-template-react">Create Snowpack App React template</a> comes with everything you’ll learn in this guide plus other useful tools.</p></blockquote><h2 id="getting-started">Getting started</h2><p>The easiest way to start a new Snowpack project is with <a href="https://github.com/withastro/snowpack/tree/main/create-snowpack-app/cli">Create Snowpack App</a>, a tool to set up Snowpack in a new directory. <code>@snowpack/project-template-minimal</code> is a Create Snowpack App template for a simple, bare-bones Snowpack project setup that the rest of this guide builds on.</p><p>To get started, open your terminal and head to a directory where you want to put your new project. Now run the following command in your terminal to create a new directory called <code>react-snowpack</code> with the minimal template automatically installed.</p><pre class=""><code class="language-bash">npx create-snowpack-app react-snowpack --template @snowpack/app-template-minimal </code></pre><p>You can now head to the new directory and start Snowpack with the following two commands:</p><pre class=""><code class="language-bash"><span class="token builtin class-name">cd</span> react-snowpack <span class="token function">npm</span> run start </code></pre><p>You should see your new website up and running!</p><blockquote><p>💡 Tip: the <code>README.md</code> in your new project contains useful information about what each file does.</p></blockquote><div class="frame"><img src="/img/guides/react/minimalist-hello-world.png" alt="screenshot of project-template-minimal, which shows 'Hello world' in text on a white background." class="screenshot"></div><p>Now that you have a basic project up and running, to install React, run the following command in your project directory:</p><pre class=""><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> react react-dom --save </code></pre><blockquote><p>💡 Tip: add the <code>--use-yarn</code> or <code>--use-pnpm</code> flag to use something other than npm</p></blockquote><h2 id="create-your-first-react-component">Create your first React component</h2><p>React relies on a special templating language called JSX. If you’re familiar with React then you already know JSX: it’s React’s templating language that allows you to write something like <code>&lt;App /&gt;</code> or <code>&lt;Header&gt;&lt;/Header&gt;</code> directly in your JavaScript code.</p><p>Snowpack has built in support for JSX in files using the <code>.jsx</code> extension. That means that there’s no plugins or configuration needed to write your first React component. Rename <code>index.js</code> file to <code>index.jsx</code> so that Snowpack knows to handle JSX in the file:</p><pre class=""><code class="language-bash"><span class="token function">mv</span> index.js index.jsx </code></pre><blockquote><p>💡 Tip: you do not need to update your <code>index.html</code> script tag reference to point to <code>index.jsx</code>. Browsers don’t speak JSX (or TypeScript, for that matter), so any compile-to-JS file formats compile to <code>.js</code> in the final browser build. This is good to keep in mind when you’re referencing built files in HTML <code>&lt;script src=""&gt;</code> and <code>&lt;link href=""&gt;</code> elements.</p></blockquote><p>You can now import React in <code>index.jsx</code> and add a simple test component just to make sure it’s working:</p><pre class=""><code class="language-diff"><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> /* Add JavaScript code here! */ </span></span><span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line"> console.log('Hello World! You did it! Welcome to Snowpack :D'); </span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> import React from 'react'; </span><span class="token prefix inserted">+</span><span class="token line"> import ReactDOM from 'react-dom'; </span><span class="token prefix inserted">+</span><span class="token line"> ReactDOM.render(&lt;div&gt;"HELLO REACT"&lt;/div&gt;, document.getElementById('root')); </span></span></code></pre><p>Since the React code is rendering into an element with the ID <code>root</code>, you’ll need to add that to <code>index.html</code>:</p><pre class=""><code class="language-diff"><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> &lt;body&gt; </span></span><span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line"> &lt;h1&gt;Welcome to Snowpack!&lt;/h1&gt; </span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> &lt;div id="root"&gt;&lt;/div&gt; </span></span><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> &lt;script type="module" src="/index.js"&gt;&lt;/script&gt; </span><span class="token prefix unchanged"> </span><span class="token line"> &lt;/body&gt; </span></span></code></pre><div class="frame"><img src="/img/guides/react/minimalist-hello-world-react.png" alt="screenshot of the project, which shows 'HELLO REACT' on a white background" class="screenshot"></div><p>You’ve just created your first React component in Snowpack!</p><h2 id="customize-your-project-layout">Customize your project layout</h2><p>Since you’ll be adding a bunch of new files, you probably don’t want them crowding up your top-level root directly. Snowpack is flexible enough to support whatever project layout that you prefer. In this guide, you’ll learn how to use a popular project pattern from the React community.</p><pre class=""><code class="language-html">📁 src : your React components and their assets (CSS, images) ↳ index.jsx 📁 public : global assets like images, fonts, icons, and global CSS ↳ index.css ↳ index.html </code></pre><p>Use your favorite visual editor to rearrange and rename, or run these commands in the terminal:</p><pre class=""><code class="language-bash"><span class="token function">mkdir</span> src <span class="token function">mkdir</span> public <span class="token function">mv</span> index.jsx src/index.jsx <span class="token function">mv</span> index.html public/index.html <span class="token function">mv</span> index.css public/index.css </code></pre><p>This means if you are running Snowpack right now, the site is now broken as the files are all in different places. Lets add a “mount” configuration to update your site to your new project layout.</p><p>The <code>mount</code> configuration changes where Snowpack looks for and builds files. Every Snowpack project comes with a <code>snowpack.config.mjs</code> file for any configuration that you might need. Right now, you should see a configuration file with empty options. Add this to the empty <code>mount</code> object:</p><pre class=""><code class="language-diff"><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> export default { </span><span class="token prefix unchanged"> </span><span class="token line"> mount: { </span></span><span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line"> /* ... */ </span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> // directory name: 'build directory' </span><span class="token prefix inserted">+</span><span class="token line"> public: '/', </span><span class="token prefix inserted">+</span><span class="token line"> src: '/dist', </span></span><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> }, </span><span class="token prefix unchanged"> </span><span class="token line"> }; </span></span></code></pre><img src="/img/guides/react/folderstructure.png" alt="The original file configuration had Snowpack building the directory structure the same as the directories in the project, including root. Now the config builds only src and public. Src to the dist folder and public to root."><p><code>mount</code> is part of the <a href="/reference/configuration">Snowpack Configuration API</a>. It allows you to customize the file structure of your project. The key is the name of the directory and the value is where you’d like them in the final build. With this new configuration, Snowpack builds files in <code>public</code> like <code>public/index.css</code> directory into <code>index.css</code>. It builds files in <code>src</code> like <code>src/index.js</code> into <code>/dist/index.js</code>, so you’ll need to change that path in your <code>index.html</code>:</p><pre class=""><code class="language-diff"><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> &lt;body&gt; </span><span class="token prefix unchanged"> </span><span class="token line"> &lt;h1&gt;Welcome to Snowpack!&lt;/h1&gt; </span><span class="token prefix unchanged"> </span><span class="token line"> &lt;div id="root"&gt;&lt;/div&gt; </span></span><span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line"> &lt;script type="module" src="/index.js"&gt;&lt;/script&gt; </span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> &lt;script type="module" src="/dist/index.js"&gt;&lt;/script&gt; </span></span><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> &lt;/body&gt; </span></span></code></pre><p>You’ll need to restart Snowpack for configuration file changes. When you start up again, if it worked, it should look the same.</p><p>Create a new file at <code>src/App.jsx</code> and paste the following code into this new file to create an <code>App</code> component:</p><pre class=""><code class="language-jsx"><span class="token keyword">import</span> React<span class="token punctuation">,</span> <span class="token punctuation">{</span>useState<span class="token punctuation">,</span> useEffect<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">App</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Create the count state.</span> <span class="token keyword">const</span> <span class="token punctuation">[</span>count<span class="token punctuation">,</span> setCount<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Update the count (+1 every second).</span> <span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> timer <span class="token operator">=</span> <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">setCount</span><span class="token punctuation">(</span>count <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">clearTimeout</span><span class="token punctuation">(</span>timer<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>count<span class="token punctuation">,</span> setCount<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Return the App component.</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>App<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>App-header<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span><span class="token plain-text"> Page has been open for </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>code</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>count<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>code</span><span class="token punctuation">&gt;</span></span><span class="token plain-text"> seconds. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">&gt;</span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> App<span class="token punctuation">;</span> </code></pre><p>Now include it in <code>index.jsx</code></p><pre class=""><code class="language-diff"><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> import React from 'react'; </span><span class="token prefix unchanged"> </span><span class="token line"> import ReactDOM from 'react-dom'; </span></span><span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line"> ReactDOM.render(&lt;div&gt;"HELLO WORLD"&lt;/div&gt;, document.getElementById('root')); </span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> import App from './App.jsx'; </span><span class="token prefix inserted">+</span><span class="token line"> ReactDOM.render( </span><span class="token prefix inserted">+</span><span class="token line"> &lt;React.StrictMode&gt; </span><span class="token prefix inserted">+</span><span class="token line"> &lt;App /&gt; </span><span class="token prefix inserted">+</span><span class="token line"> &lt;/React.StrictMode&gt;, </span><span class="token prefix inserted">+</span><span class="token line"> document.getElementById('root'), </span><span class="token prefix inserted">+</span><span class="token line"> ); </span></span></code></pre><blockquote><p>💡 Tip: <a href="https://reactjs.org/docs/strict-mode.html">Strict Mode</a> is a tool for highlighting potential problems in React code.</p></blockquote><p>You shouldn’t need to restart Snowpack to see this, it should look like this:</p><div class="frame"><img src="/img/guides/react/minimalist-hello-world-react-timer.png" alt="screenshot of the project with text that says 'Page has been open for' and the number of seconds then 'seconds'" class="screenshot"></div><h2 id="styling-your-project">Styling your project</h2><p>When you add assets like images or CSS, Snowpack includes them in your final build. If you already know React, this process should look pretty familiar.</p><blockquote><p>💡 Tip: as you’re doing this, you should not need to reload the page or restart Snowpack. Snowpack automatically updates the project in the browser as you edit code.</p></blockquote><p>Add this file <a href="https://github.com/withastro/snowpack/blob/main/create-snowpack-app/app-template-react/src/logo.svg"><code>logo.svg</code></a> to your <code>src</code> directory. Now you can import it into your <code>App.jsx</code> and use it in an <code>img</code> tag to display it.</p><pre class=""><code class="language-diff"><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> import React, { useState, useEffect } from 'react'; </span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> import logo from './logo.svg'; </span></span> <span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> function App() { </span><span class="token prefix unchanged"> </span><span class="token line"> // Create the count state. </span><span class="token prefix unchanged"> </span><span class="token line"> const [count, setCount] = useState(0); </span><span class="token prefix unchanged"> </span><span class="token line"> // Create the counter (+1 every second). </span><span class="token prefix unchanged"> </span><span class="token line"> useEffect(() =&gt; { </span><span class="token prefix unchanged"> </span><span class="token line"> const timer = setTimeout(() =&gt; setCount(count + 1), 1000); </span><span class="token prefix unchanged"> </span><span class="token line"> return () =&gt; clearTimeout(timer); </span><span class="token prefix unchanged"> </span><span class="token line"> }, [count, setCount]); </span><span class="token prefix unchanged"> </span><span class="token line"> // Return the App component. </span><span class="token prefix unchanged"> </span><span class="token line"> return ( </span><span class="token prefix unchanged"> </span><span class="token line"> &lt;div className="App"&gt; </span><span class="token prefix unchanged"> </span><span class="token line"> &lt;header className="App-header"&gt; </span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> &lt;img src={logo} className="App-logo" alt="logo" /&gt; </span></span><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> &lt;p&gt; </span></span></code></pre><div class="frame"><img src="/img/guides/react/minimalist-hello-world-react-logo.png" alt="the React logo (a blue atom) is now at the top of the page" class="screenshot"></div><p>The project already has index.css for global styles. For CSS that’s only for a specific component, a common design pattern is to add it in a CSS file with the same base name as the component. The style file for <code>App.jsx</code> would be <code>App.css</code> with this pattern.</p><blockquote><p>💡 Tip: Snowpack has built-in support for <a href="/reference/supported-files">CSS Modules</a> and if you’d like to use Sass there is an official <a href="/guides/sass/">Sass Plugin</a>.</p></blockquote><p>Create <code>src/App.css</code> and add this CSS:</p><pre class=""><code class="language-css"><span class="token selector">.App</span> <span class="token punctuation">{</span> <span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.App p</span> <span class="token punctuation">{</span> <span class="token property">margin</span><span class="token punctuation">:</span> 0.4rem<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.App-logo</span> <span class="token punctuation">{</span> <span class="token property">height</span><span class="token punctuation">:</span> 40vmin<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-reduced-motion</span><span class="token punctuation">:</span> no-preference<span class="token punctuation">)</span></span> <span class="token punctuation">{</span> <span class="token selector">.App-logo</span> <span class="token punctuation">{</span> <span class="token property">animation</span><span class="token punctuation">:</span> App-logo-spin infinite 20s linear<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token selector">.App-header</span> <span class="token punctuation">{</span> <span class="token property">background-color</span><span class="token punctuation">:</span> #282c34<span class="token punctuation">;</span> <span class="token property">min-height</span><span class="token punctuation">:</span> 100vh<span class="token punctuation">;</span> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span> <span class="token property">flex-direction</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span> <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span> <span class="token property">justify-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span> <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>10px + 2vmin<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@keyframes</span> App-logo-spin</span> <span class="token punctuation">{</span> <span class="token selector">from</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span>0deg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">to</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span>360deg<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </code></pre><p>To use this CSS, head to <code>App.jsx</code> and import it</p><pre class=""><code class="language-diff"><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> import logo from './logo.svg'; </span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> import './App.css'; </span></span></code></pre><div class="frame"><img src="/img/guides/react/react.gif" alt="The page now has centered items, a grey background, styled fonts, and the React logo has an animation that rotates it." class="screenshot"></div><h2 id="making-snowpack-even-faster-with-fast-refresh">Making Snowpack Even Faster with Fast Refresh</h2><p><a href="https://reactnative.dev/docs/fast-refresh">React Fast Refresh</a>? What’s that? It’s a Snowpack enhancement that lets you push individual file changes to update the browser without refreshing the page or clearing component state.</p><p>React projects are often interactive and include state. For example, this project you’re building has a state that is the amount of time on the page. When developing with state it’s useful not to lose it while you edit code. React Fast Refresh shows you updates without refreshing the entire page. Showing you how to add this is also a good intro to Snowpack plugins. Snowpack starts with a minimal setup with the perspective that you can add what you need through the plugin system.</p><p>Start by enabling <a href="/concepts/hot-module-replacement">Hot Module Replacement</a> in your project. HMR is the system that lets Snowpack push updates to the browser without a full page refresh, a requirement for Fast Refresh. You can enable HMR for React by adding a small snippet of code to your <code>src/index.jsx</code> file.</p><pre class=""><code class="language-diff"><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> ReactDOM.render( </span><span class="token prefix unchanged"> </span><span class="token line"> &lt;React.StrictMode&gt; </span><span class="token prefix unchanged"> </span><span class="token line"> &lt;App /&gt; </span><span class="token prefix unchanged"> </span><span class="token line"> &lt;/React.StrictMode&gt;, </span><span class="token prefix unchanged"> </span><span class="token line"> document.getElementById('root'), </span><span class="token prefix unchanged"> </span><span class="token line"> ); </span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> // Hot Module Replacement (HMR) - Remove this snippet to remove HMR. </span><span class="token prefix inserted">+</span><span class="token line"> // Learn more: https://www.snowpack.dev/concepts/hot-module-replacement </span><span class="token prefix inserted">+</span><span class="token line"> if (undefined /* [snowpack] import.meta.hot */ ) { </span><span class="token prefix inserted">+</span><span class="token line"> undefined /* [snowpack] import.meta.hot */ .accept(); </span><span class="token prefix inserted">+</span><span class="token line"> } </span></span></code></pre><p>Now when you change <code>App.jsx</code> the page updates to show your changes without a full refresh.</p><div class="frame"><img src="/img/guides/react/hmr.gif" alt="GIF showing code side by side with the app. A change in made to App.jsx and it shows immediately when the file is changed. The counter keeps counting uninterrupted." class="screenshot"></div><p>HMR can save you time on its own, but you may notice in the example above that the counter on the page still resets to 0. This can slow down your development, especially when you’re trying to debug a specific component state problem. Lets enable Fast Refresh to preserve component state across updates.</p><p>To enable Fast Refresh, you’ll need to install the <code>@snowpack/plugin-react-refresh</code> package. This package is a Snowpack plugin, which you can use to enhance or customize Snowpack with all sorts of new behaviors. To start, install the package in your project:</p><pre class=""><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> @snowpack/plugin-react-refresh --save-dev </code></pre><p>Once installed, you’ll need to add the plugin to your Snowpack configuration file so that Snowpack knows to use it:</p><pre class=""><code class="language-diff"><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> module.exports = { </span><span class="token prefix unchanged"> </span><span class="token line"> mount: { </span><span class="token prefix unchanged"> </span><span class="token line"> public: '/', </span><span class="token prefix unchanged"> </span><span class="token line"> src: '/dist', </span><span class="token prefix unchanged"> </span><span class="token line"> }, </span></span><span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line"> plugins: [] </span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> plugins: ['@snowpack/plugin-react-refresh'], </span></span><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line"> }; </span></span></code></pre><p>Restart Snowpack to apply the new plugin, and then try changing the <code>App.jsx</code> component again. If Fast Refresh is working properly, the counter keeps its value across changes, without resetting to zero.</p><div class="frame"><img src="/img/guides/react/react-fast-refresh.gif" alt="GIF showing code side by side with the app. A change in made to App.jsx and it shows immediately when the file is changed. The counter keeps counting uninterrupted." class="screenshot"></div><h2 id="going-further">Going further</h2><p>Great job! You’re now ready to build the React project of your dreams with Snowpack. Want to tweet your accomplishment to the world? Click the button below:</p><p><a href="https://twitter.com/share?ref_src=twsrc%5Etfw" class="twitter-share-button" data-text="I just learned how to build a React app with #Snowpack. Check out the tutorial:" data-show-count="false">Tweet</a><script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></p><p>At this point you have the basics and have a great starter for any React project. But if you compare with the official <a href="https://github.com/withastro/snowpack/tree/main/create-snowpack-app/app-template-react">Snowpack React template</a> you’ll notice it has some other developer tools you might find useful:</p><ul><li><a href="https://prettier.io/">Prettier</a> — a popular code formatter</li><li><a href="/guides/testing">Tests</a> — Snowpack supports any popular JavaScript testing framework</li><li><a href="https://github.com/withastro/snowpack/tree/main/plugins/plugin-dotenv"><code>@snowpack/plugin-dotenv</code></a> — Use <code>dotenv</code> in your Snowpack. This is useful for environment specific variables</li></ul><p>If you’d like to use Typescript with Snowpack and React, check out the <a href="https://github.com/withastro/snowpack/tree/main/create-snowpack-app/app-template-react-typescript">Snowpack React Typescript</a> starter.</p><p>If you have any questions, comments, or corrections, we’d love to hear from you in the Snowpack <a href="https://github.com/withastro/snowpack/discussions">discussion</a> forum or our <a href="https://discord.gg/rS8SnRk">Snowpack Discord community</a>.</p></div></div></div></article></section></div><script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-130280175-9"></script><script> window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'UA-130280175-9', { page_path: location.pathname === '/' ? (location.pathname + location.hash) : (location.pathname) }); </script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10