<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ely Lucas</title>
    <description>The latest articles on DEV Community by Ely Lucas (@elylucas).</description>
    <link>https://dev.to/elylucas</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F184099%2F48f060ae-9cd5-4fa7-b359-73d292331452.jpeg</url>
      <title>DEV Community: Ely Lucas</title>
      <link>https://dev.to/elylucas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/elylucas"/>
    <language>en</language>
    <item>
      <title>Introducing ts-dash: A Quick and Simple TypeScript Project Creator</title>
      <dc:creator>Ely Lucas</dc:creator>
      <pubDate>Wed, 18 Feb 2026 17:46:05 +0000</pubDate>
      <link>https://dev.to/elylucas/introducing-ts-dash-a-quick-and-simple-typescript-project-creator-53k0</link>
      <guid>https://dev.to/elylucas/introducing-ts-dash-a-quick-and-simple-typescript-project-creator-53k0</guid>
      <description>&lt;h2&gt;
  
  
  The Itch
&lt;/h2&gt;

&lt;p&gt;Lately I've been creating a lot of throwaway TypeScript projects. You know that feeling. You have an idea for a quick script, a small API, or just want to try something out in TypeScript. So you create a folder, run &lt;code&gt;npm init&lt;/code&gt;, install TypeScript, set up a &lt;code&gt;tsconfig.json&lt;/code&gt;, configure your build step, add a watch script, try a bunch of different options to get the module system right, and by the time you're done you've forgotten what you were trying to build in the first place.&lt;/p&gt;

&lt;p&gt;I've done this dance a hundred times. And every time, I think "I should really automate this." So I finally did.&lt;/p&gt;

&lt;p&gt;The amazing part? I did it all in &lt;a href="https://claude.ai/code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; (right after Opus 4.6 dropped), literally going from idea to a working scaffolding tool in about an hour. It was a blast. I told Claude exactly what I wanted, and it generated the entire project for me. I just had to tweak a few things here and there.&lt;/p&gt;

&lt;h2&gt;
  
  
  create-ts-dash
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/elylucas/create-ts-dash" rel="noopener noreferrer"&gt;create-ts-dash&lt;/a&gt; is a simple scaffolding tool that gets you from zero to a working TypeScript project in one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create ts-dash my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. You get a clean TypeScript project with sensible defaults, ready to go. No boilerplate fatigue, no decision paralysis. The project is created from scratch each time, so you always get the latest package versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Get
&lt;/h2&gt;

&lt;p&gt;Every project comes with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; with a strict config (because we're not animals)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tsx&lt;/strong&gt; for running TypeScript directly with file watching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ESM modules&lt;/strong&gt; configured out of the box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git&lt;/strong&gt; initialized with a &lt;code&gt;.gitignore&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Need a Web Server?
&lt;/h2&gt;

&lt;p&gt;If you're building an API or a server, create-ts-dash lets you pick from three frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Express&lt;/strong&gt; — the one you already know&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fastify&lt;/strong&gt; — fast and low overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hono&lt;/strong&gt; — lightweight and ultrafast&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Optional Tooling
&lt;/h2&gt;

&lt;p&gt;You can also opt into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ESLint + Prettier&lt;/strong&gt; for linting and formatting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vitest&lt;/strong&gt; for testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing is forced on you. Pick what you need, skip what you don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Doesn't This Already Exist?"
&lt;/h2&gt;

&lt;p&gt;Yes. Probably. Almost certainly. I didn't do extensive research, and I don't care. Sometimes you want to build something that works exactly the way &lt;em&gt;you&lt;/em&gt; think it should. Something where you know every line of code because you wrote it. Something that scratches your specific itch.&lt;/p&gt;

&lt;p&gt;I built create-ts-dash for me. If it's useful to you too, that's awesome. If you prefer something else, that's cool too. The world has room for another scaffolding tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# with npm&lt;/span&gt;
npm create ts-dash my-app

&lt;span class="c"&gt;# with yarn&lt;/span&gt;
yarn create ts-dash my-app

&lt;span class="c"&gt;# with pnpm&lt;/span&gt;
pnpm create ts-dash my-app

&lt;span class="c"&gt;# with bun&lt;/span&gt;
bun create ts-dash my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or run it without a project name for interactive mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create ts-dash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the repo at &lt;a href="https://github.com/elylucas/create-ts-dash" rel="noopener noreferrer"&gt;github.com/elylucas/create-ts-dash&lt;/a&gt;, give it a spin, and let me know what you think.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>cli</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Introducing the New Overlay Hooks for Ionic React</title>
      <dc:creator>Ely Lucas</dc:creator>
      <pubDate>Mon, 09 Aug 2021 21:48:10 +0000</pubDate>
      <link>https://dev.to/ionic/introducing-the-new-overlay-hooks-for-ionic-react-190i</link>
      <guid>https://dev.to/ionic/introducing-the-new-overlay-hooks-for-ionic-react-190i</guid>
      <description>&lt;p&gt;Hello Friends! We know everyone is excited about the new features in Ionic Framework 6.0 beta, but that doesn't mean we're done with V5! In Ionic React 5.6, we packaged up a new set of hooks for controlling our overlay components that we think you might like. What is an overlay you ask? It’s the term we give components that display over your current content, such as alerts, modals, toasts, etc. &lt;/p&gt;

&lt;p&gt;In this post, I’m going to go over how to use the new hooks to display these components in your apps. But first, let's do a quick recap on how overlays worked before, and some of the challenges that you might encounter with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overlay Components
&lt;/h2&gt;

&lt;p&gt;Each of the overlays has a React component that is exported from &lt;code&gt;@ionic/react&lt;/code&gt;, such as &lt;code&gt;IonAlert&lt;/code&gt; and &lt;code&gt;IonLoading&lt;/code&gt;.  These components take a prop called &lt;code&gt;isOpen&lt;/code&gt;, which determines if the overlay should be displayed or not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonAlert&lt;/span&gt;
  &lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;showAlert&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These components are great and they aren’t going away. The new hooks don't deprecate them in any way. However, there's some ceremony you have to observe to use them. &lt;/p&gt;

&lt;p&gt;First, managing the state that handles the &lt;code&gt;isOpen&lt;/code&gt; prop could be a challenge. If the state was contained in the component that the overlay was in, then it was no problem (like a local state variable from &lt;code&gt;useState&lt;/code&gt;). However, if the action to display the overlays was in a different part of the app, then you needed to use some state management technique or library to pass the &lt;code&gt;isOpen&lt;/code&gt; prop along.&lt;/p&gt;

&lt;p&gt;Second, each of the Overlay components required you to provide an &lt;code&gt;onDidDismiss&lt;/code&gt; callback that would flip the &lt;code&gt;isOpen&lt;/code&gt; prop back to false in case the overlay dismissed itself somehow. Not overriding it would cause &lt;code&gt;isOpen&lt;/code&gt; to stay true, and if your app tried to show the overlay again, then you would be setting true to true, and Ionic wouldn’t detect the change and, thus, wouldn't display it again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonAlert&lt;/span&gt;
  &lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;showAlert&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello there friend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nx"&gt;onDidDismiss&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setShowAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A third challenge was that it was difficult to display an overlay from outside of your component; for instance, if you wanted to display an alert when an API request failed. Ionic Angular had controller instances you could import to use in services to display overlays. In React, this was more difficult to do.&lt;/p&gt;

&lt;p&gt;The new overlay hooks in Ionic React 5.6 help with all these scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overlay Hooks
&lt;/h2&gt;

&lt;p&gt;The goal of the overlay hooks was to reduce this process and to make the usage of the overlays feel more natural for those writing functional React components.&lt;/p&gt;

&lt;p&gt;To get started with the hooks, you import the hook for the overlay you want to use from &lt;code&gt;@ionic/react&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useIonAlert&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, in your functional component, you call the hook to get back the show and hide methods, which get returned in an array similar to how &lt;code&gt;useState&lt;/code&gt; returns its members. We choose the array method of destructuring so naming the methods is easier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;showAlert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hideAlert&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useIonAlert&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Most overlays dismiss themselves when the user is done interacting with them, so you won’t need the hide method for most scenarios.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then to display the overlay, you call the show method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;showAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://blog.ionicframework.com/wp-content/uploads/2021/07/alert-overlay.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m01dmt6I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2021/07/alert-overlay.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The alert will dismiss itself after the user clicks the "Ok" button. You don’t need to worry about doing any additional work when the overlays go away.&lt;/p&gt;

&lt;p&gt;Most of the overlay hooks have options that you can pass to them. The more common options can be passed in as additional parameters. For instance, the toast overlay's show method also takes the time to display the toast as its second parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;showToast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello from a toast!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will display the toast for three seconds, and then the toast will dismiss itself.&lt;/p&gt;

&lt;p&gt;More options can be sent by passing in an object instead of the separate parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;showToast&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;buttons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hide&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;hideToast&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, click hide to dismiss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;onDidDismiss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dismissed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;              
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;React hooks can be used in other hooks as well. Because of this, it's possible to display an overlay from within other hooks, such as one responsible for fetching some data. Here is a sample custom hook to fetch a customer that displays a toast when the request fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useCustomer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCustomer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Customer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;showToast&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useIonToast&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;fetchCustomer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fetchCustomer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`myapi.com/customers/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;setCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;showToast&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Oops, unable to get customer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;warning&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;buttons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;close&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;showToast&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  More Info
&lt;/h2&gt;

&lt;p&gt;To learn more about the new hooks, visit our &lt;a href="https://ionicframework.com/docs/react/overlays"&gt;guide on using overlays in Ionic React&lt;/a&gt;, and check out the docs for each of the overlay components for usage examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ionicframework.com/docs/api/action-sheet"&gt;Action Sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ionicframework.com/docs/api/modal#usage"&gt;Alert&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ionicframework.com/docs/api/loading"&gt;Loading&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ionicframework.com/docs/api/modal"&gt;Modal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ionicframework.com/docs/api/picker"&gt;Picker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ionicframework.com/docs/api/popover"&gt;Popover&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ionicframework.com/docs/api/toast"&gt;Toast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let us know in the comments down below if you have any feedback, and until next time, happy coding!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Reduce the Number of Renders in an Ionic React App</title>
      <dc:creator>Ely Lucas</dc:creator>
      <pubDate>Thu, 28 Jan 2021 15:45:05 +0000</pubDate>
      <link>https://dev.to/ionic/how-to-reduce-the-number-of-renders-in-an-ionic-react-app-26mm</link>
      <guid>https://dev.to/ionic/how-to-reduce-the-number-of-renders-in-an-ionic-react-app-26mm</guid>
      <description>&lt;p&gt;Hello friends!&lt;/p&gt;

&lt;p&gt;Welcome to this first article in a series that I plan on doing around quick tips for routing in Ionic React.&lt;/p&gt;

&lt;p&gt;To kick this series off, I'm going to answer one of the top questions I get, and that's "Why do my Ionic Pages render so much?"&lt;/p&gt;

&lt;p&gt;For starters, you shouldn't be overly concerned with multiple renders. During the render phase, React will build up a representation of your component in what is called the Virtual DOM. Building this VDOM is relatively cheap. Afterward, React will compare the VDOM to what is in the actual DOM, and it will only do the costly DOM updates if there are actual changes.&lt;/p&gt;

&lt;p&gt;This means that if your component renders again, but there aren’t any changes, then the cost is minimal.&lt;/p&gt;

&lt;p&gt;However, there are a couple of drawbacks to rendering too much. First, React must construct the VDOM and run its diffing algorithm for each render call. While this is highly optimized and fast, doing so is wasteful if the component doesn’t need to update. Second, if you have costly code in the render method, it will get run each time. &lt;/p&gt;

&lt;p&gt;Also, if you are like me, it can drive you bonkers.&lt;/p&gt;

&lt;p&gt;Therefore, trying to cut down on the number of renders is a micro-optimization, but sometimes it’s one worth taking.&lt;/p&gt;

&lt;p&gt;In this article, we will take a look at why render gets called multiple times, and then I'll show you some techniques you can use in your own apps to help reduce them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;If you want to follow along, start up a new Ionic React app using the &lt;a href="https://ionicframework.com/docs/intro/cli" rel="noopener noreferrer"&gt;Ionic CLI&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ionic start RenderTest tabs &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;react 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the Tab1.tsx file, add a log statement inside of the function so we can see each time the component is rendered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Tab1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tab1 render&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonPage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonPage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fire up the app with &lt;code&gt;ionic serve&lt;/code&gt;, and you will see that, on initial load, the Tab1 page renders twice:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi33gvnx1huo6vblmgm92.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi33gvnx1huo6vblmgm92.png" alt="initial-render"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, if you click around on the tabs, you will see that Tab1 renders 3-4 times each time you switch a tab! What's going on here?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you render so much?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzldjszkxtfzc0yetpsr7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzldjszkxtfzc0yetpsr7.jpg" alt="yodawgrender"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every page in an Ionic React app is wrapped with an &lt;code&gt;IonPage&lt;/code&gt; component. The IonPage not only provides some div containers for holding your page and styles around it, but it is also very important when it comes to doing page transitions. &lt;/p&gt;

&lt;p&gt;There is some semi-complicated logic that goes on in the background in regards to getting a reference to these IonPages when they are transitioned to and from. Unfortunately, to get the reference, we have to wait until the component mounts, then grab the reference, and store that reference in context.  This all happens in the IonRouterOutlet component, but when this component renders, it often causes its children (your routes and IonPages) to render along with it.&lt;/p&gt;

&lt;p&gt;This is why you might see an IonPage render two or three times when it is displayed the first time.&lt;/p&gt;

&lt;p&gt;Next, you might notice that some pages might render when they are not even in the current view.&lt;/p&gt;

&lt;p&gt;To provide some of the smooth transitions and to maintain the state of views that you left but might come back to, we don't actually unmount your component in some scenarios (like navigating between tabs). Instead, when your page transitions out of view, we hide it via CSS. The component is still mounted and can still render. Therefore, if the component gets any new props passed into it, it will render.&lt;/p&gt;

&lt;p&gt;By default, the routes are setup to pass in the your IonPage in the component prop of a route, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/tab1"&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Tab1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;exact&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using the component prop, React Router will pass in some props on your behalf, like &lt;code&gt;location&lt;/code&gt; and &lt;code&gt;history&lt;/code&gt;. Each time you make a navigation change, these route props will change, which will cause all your IonPages that are currently mounted to render again.&lt;/p&gt;

&lt;p&gt;This can get a bit out of hand, so let’s take a look at a few strategies to cut down on the excessive renders.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing the renders
&lt;/h2&gt;

&lt;p&gt;So there are two main culprits here. Parent components that are rendering that cause its children to render, and new props being passed in via routing that causes another render.&lt;/p&gt;

&lt;p&gt;Let's deal with parent components rendering first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Component Memoization
&lt;/h3&gt;

&lt;p&gt;In React class-based components, we were able to finely control when components would render with the &lt;code&gt;shouldComponentUpdate&lt;/code&gt; lifecycle method. This method would receive the new props/state coming in, which we could compare to the old props/state and determine if we want our component to call its render method. Or, even better, we could inherit from &lt;code&gt;React.PureComponent&lt;/code&gt; and let React take care of this logic for us.&lt;/p&gt;

&lt;p&gt;This would make it so that your component would only update if its props or state change, and ignore any updates from the parent component rendering.&lt;/p&gt;

&lt;p&gt;To accomplish something similar in a React Functional Component (FC), we can wrap our FC in &lt;code&gt;React.memo&lt;/code&gt;, which will &lt;a href="https://en.wikipedia.org/wiki/Memoization" rel="noopener noreferrer"&gt;memoize&lt;/a&gt; your component and store a cached version of it based on the props that are passed into it.&lt;/p&gt;

&lt;p&gt;To do so, I like to wrap the FC as its being exported with &lt;code&gt;React.memo&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Tab1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should notice that now the number of renders is cut down quite a bit when you navigate between the tabs.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;React.memo&lt;/code&gt; is a good way to cut down on needless renders, however, take care when doing this, as you are basically trading fewer renders for memory consumption.&lt;/p&gt;

&lt;h3&gt;
  
  
  Route Setup
&lt;/h3&gt;

&lt;p&gt;Next, let’s modify our routes so that React Router no longer passes in the Route Component props into our Tab1 page.&lt;/p&gt;

&lt;p&gt;In the route setup, we are using the &lt;code&gt;component&lt;/code&gt; prop, and React Router will pass in all the route props every time there is a change in navigation. &lt;/p&gt;

&lt;p&gt;As of React Router 5.1, there is a new method to specify which component to render when the route matches, &lt;a href="https://reacttraining.com/blog/react-router-v5-1/#staying-ahead-of-the-curve" rel="noopener noreferrer"&gt;and this method is encouraged going forward&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The new method is to pass your component in as a child to the route like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/tab1"&lt;/span&gt; &lt;span class="na"&gt;exact&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tab1&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if you check the logs, you will see that the &lt;code&gt;Tab1&lt;/code&gt; page only renders once on its initial load, and has no additional renders when navigating between the tabs. Nice 🎉!&lt;/p&gt;

&lt;p&gt;"But what if I need the routing props in my component?" I hear you ask.&lt;/p&gt;

&lt;p&gt;React Router has you covered there as well. There are several new &lt;a href="https://reacttraining.com/blog/react-router-v5-1/#meet-react-router-51" rel="noopener noreferrer"&gt;React Hooks available&lt;/a&gt; that you can use to get access to the same props that were passed in before, namely &lt;code&gt;useParams&lt;/code&gt;, &lt;code&gt;useLocation&lt;/code&gt;, and &lt;code&gt;useHistory&lt;/code&gt;. So if you had a route setup to get an &lt;code&gt;id&lt;/code&gt; param from the path, you would access it like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* The Route: */&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/tab1/:id"&lt;/span&gt; &lt;span class="na"&gt;exact&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tab1&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="cm"&gt;/* And inside the Tab1.tsx function: */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useParams&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// do something with params.id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;With just a few quick optimizations, we were able to cut down the renders from many times on page navigation, to just a single time when the IonPage first loads. Not too shabby!&lt;/p&gt;

&lt;p&gt;Got any comments or want to see me cover something around Ionic React in the future? Hit me up in the comments below or catch me on Twitter &lt;a href="https://www.twitter.com/elylucas" rel="noopener noreferrer"&gt;@elylucas&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>ionic</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Confirm Leaving a Page in Ionic React with React Router</title>
      <dc:creator>Ely Lucas</dc:creator>
      <pubDate>Fri, 09 Oct 2020 14:00:42 +0000</pubDate>
      <link>https://dev.to/ionic/how-to-confirm-leaving-a-page-in-ionic-react-with-react-router-p55</link>
      <guid>https://dev.to/ionic/how-to-confirm-leaving-a-page-in-ionic-react-with-react-router-p55</guid>
      <description>&lt;p&gt;Have you ever been in the middle of an important task on a page, like filling out a form, and accidentally left and lost all your work? That bites! &lt;/p&gt;

&lt;p&gt;And it's a bad experience for your users, especially on mobile.&lt;/p&gt;

&lt;p&gt;A typical technique is to confirm with the user if they do want to leave the page with a confirmation dialog. In this post, I'll show you how to do so in an Ionic React application, and how you can customize the confirmation UI to fit your particular app. Let's go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Using React Router's Prompt Component
&lt;/h2&gt;

&lt;p&gt;An Ionic React application uses &lt;a href="https://reactrouter.com/" rel="noopener noreferrer"&gt;React Router&lt;/a&gt; for all of its navigation, and, fortunately, React Router has good support for prompting the user on navigation with their &lt;a href="https://reactrouter.com/web/api/Prompt" rel="noopener noreferrer"&gt;&lt;code&gt;Prompt&lt;/code&gt;&lt;/a&gt; component. With Prompt, a confirmation box pops up asking the user if they want to leave the page. If they click yes, the navigation takes place, and if they click no, they are left on the current page.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Prompt&lt;/code&gt; component takes two props, a &lt;code&gt;message&lt;/code&gt; to display, and a &lt;code&gt;when&lt;/code&gt; boolean to activate it.&lt;/p&gt;

&lt;p&gt;Here is a simple IonPage with a form that utilizes the Prompt component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Tab1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useIonViewWillLeave&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonPage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonHeader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonToolbar&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonTitle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Tab 1&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonTitle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonToolbar&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonHeader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonContent&lt;/span&gt; &lt;span class="na"&gt;fullscreen&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonInput&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Name"&lt;/span&gt;
          &lt;span class="na"&gt;onIonChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonInput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonButton&lt;/span&gt;
          &lt;span class="na"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"block"&lt;/span&gt;
          &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Submit
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Prompt&lt;/span&gt;
          &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"You have unsaved changes, are you sure you want to leave?"&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonContent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonPage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Prompt is imported from 'react-router'&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To determine if the form is "dirty" (if the form has been modified), we check to see if the &lt;code&gt;IonInput&lt;/code&gt; has a value or not. This is a simple method, and you will probably need to expand on the concept in your app. Many form libraries provide a way to determine if the form has been modified as well.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;useIonViewWillLeave&lt;/code&gt; hook, when a user leaves the page, we set the value of text back to a blank string to "reset" the form. This keeps the prompt from showing on other pages.&lt;/p&gt;

&lt;p&gt;Now, if we try to leave the form, by say, accidentally tapping one of the other tab buttons, we get a nice confirmation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcsppmi5rsxrk2reu2udf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcsppmi5rsxrk2reu2udf.jpg" alt="Image of form with standard browser confirm dialog"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is fairly functional as is. The confirm dialog on mobile devices looks decent, but if you want to customize the UI, we will dive into that next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing the Confirm UI
&lt;/h2&gt;

&lt;p&gt;Instead of showing the built-in confirm dialog that comes with the browser, you might want to display something a bit more custom to match the look and feel of your app. To change it up, we will use an &lt;a href="https://ionicframework.com/docs/api/alert" rel="noopener noreferrer"&gt;&lt;code&gt;IonAlert&lt;/code&gt;&lt;/a&gt; with custom buttons for the confirmation.&lt;/p&gt;

&lt;p&gt;React Router provides a way to tie into the process by passing in a &lt;code&gt;getUserConfirmation&lt;/code&gt; prop when setting up the router. In an Ionic app, we use &lt;code&gt;IonReactRouter&lt;/code&gt; and we can pass this prop here and the router will, in turn, pass the prop back down to the underlying &lt;code&gt;ReactRouter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This prop excepts a function that gets passed in the message to display, as well as a callback. The callback takes a boolean parameter to indicate if the route navigation should happen or not. We'll add it to the main &lt;code&gt;App.tsx&lt;/code&gt; page, where the routing is set up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonReactRouter&lt;/span&gt;
  &lt;span class="na"&gt;getUserConfirmation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;getUserConfirmation&lt;/code&gt; is called, we want to display an &lt;code&gt;IonAlert&lt;/code&gt; overlay with the message that was passed in. We will use a state variable to store the message. Also, we utilize a ref object to hold a reference to the callback that will be used in the alert:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;leaveConfirmMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLeaveConfirmMessage&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;confirmCallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to set them in the &lt;code&gt;getUserConfirmation&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonReactRouter&lt;/span&gt;
  &lt;span class="na"&gt;getUserConfirmation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLeaveConfirmMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;confirmCallback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we add the &lt;code&gt;IonAlert&lt;/code&gt; towards the bottom of the page, but before the closing &lt;code&gt;&amp;lt;/IonReactRouter&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonAlert&lt;/span&gt;
  &lt;span class="na"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;leaveConfirmMessage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;leaveConfirmMessage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;buttons&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;confirmCallback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;confirmCallback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;confirmCallback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;confirmCallback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;onDidDismiss&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setLeaveConfirmMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To determine if the alert is shown, we check if the confirm message has a value, and then set the message back to undefined when the alert is dismissed. In the buttons, we use the ref we set up to invoke the callback function, passing true when the user clicks "Yes", and false when "No" is clicked.&lt;/p&gt;

&lt;p&gt;And that's it! We use the &lt;code&gt;Prompt&lt;/code&gt; component as we did before in any page we want to use this custom UI. No changes are needed in our form page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fblpqtn6qyb20kkyndyoy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fblpqtn6qyb20kkyndyoy.jpg" alt="Image of form with custom dialog using IonAlert"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Browsers &lt;code&gt;beforeUnload&lt;/code&gt; Event
&lt;/h2&gt;

&lt;p&gt;One last thing we need to cover, which is what happens when the user tries to move away from the page outside of our app, like via the back button or changing the URL manually?&lt;/p&gt;

&lt;p&gt;We can use the browser's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event" rel="noopener noreferrer"&gt;&lt;code&gt;beforeUnload&lt;/code&gt;&lt;/a&gt; event for this, though it is not customizable, browser support is limited, and it requires a bit more code. However, setting it up will give our users whose browsers support it extra protection if they, say, accidentally refresh their page.&lt;/p&gt;

&lt;p&gt;Back in the page with the form, we will add a &lt;code&gt;useEffect&lt;/code&gt; hook to monitor the &lt;code&gt;text&lt;/code&gt; state. We set the &lt;code&gt;onbeforeunload&lt;/code&gt; method on the window object to a function that returns &lt;code&gt;true&lt;/code&gt; when the text has a value, and when the effect changes, set it &lt;code&gt;null&lt;/code&gt; to turn it back off:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onbeforeunload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onbeforeunload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This could also be abstracted into its own component or hook for reuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Adding some safeguards to prevent your users from accidentally leaving a page while they are performing an important task is, thankfully, pretty straight-forward in an Ionic React app thanks to the built-in support in React Router. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/elylucas/ionic-react-leave-page-confirm" rel="noopener noreferrer"&gt;I put together a demo app you can take a look at&lt;/a&gt;, feel free to check it out. Also, hit me up on twitter &lt;a class="mentioned-user" href="https://dev.to/elylucas"&gt;@elylucas&lt;/a&gt; or in the comments below if you have any questions.&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

</description>
      <category>ionic</category>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to CSS in Ionic React with Styled Components</title>
      <dc:creator>Ely Lucas</dc:creator>
      <pubDate>Thu, 10 Sep 2020 15:04:29 +0000</pubDate>
      <link>https://dev.to/ionic/how-to-css-in-ionic-react-with-styled-components-2eo6</link>
      <guid>https://dev.to/ionic/how-to-css-in-ionic-react-with-styled-components-2eo6</guid>
      <description>&lt;p&gt;When it comes to writing CSS in a React app, there are numerous options to choose from. And many people coming to Ionic React for the first time often wonder what the best approach is. The React library itself doesn't have an opinion about how styles are used in a React app, so where does one turn?&lt;/p&gt;

&lt;p&gt;By default, an Ionic React application uses plain old CSS files that are included in their corresponding components. This is a tried and true approach that has been around since the beginning of web development. However, this approach doesn't fit in well with the idea of writing encapsulated (yet extensible) components in React. &lt;/p&gt;

&lt;p&gt;One of the major drawbacks to the plain old CSS approach is that there is no built-in mechanism to scope the CSS to your particular page or component. This could lead to unintended side effects where styles bleed out of their components and affect other elements.&lt;/p&gt;

&lt;p&gt;Other areas of concern crop up around how to extend the style of a component,  and how to apply themes to a component with only plain CSS.&lt;/p&gt;

&lt;p&gt;Fortunately, there is a vibrant community in the React ecosystem that has solved a few of these issues. And since an Ionic React app is just a React app, we are free to utilize any of them in our apps!&lt;/p&gt;

&lt;p&gt;In this post, I'll be going over one of my particular favorite libraries in the space, &lt;a href="https://styled-components.com/"&gt;Styled Components&lt;/a&gt;, and show you how to get started using it to style your apps!&lt;/p&gt;

&lt;h1&gt;
  
  
  Meet Styled Components
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://styled-components.com/"&gt;Styled Components&lt;/a&gt; (SC for short from here on out) is a library that breaks down the barrier of tying styles to your React components. With SC, you don't need to declare particular CSS classes and hope they don't stomp on other classes in your project. Instead, you declare a component using the SC API, and then "attach" CSS to the component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="s2"&gt;`
  padding: 12px;
  background-color: white;
  color: blue;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you are using VS Code, make sure to download the &lt;a href="https://marketplace.visualstudio.com/items?itemName=jpoissonnier.vscode-styled-components"&gt;vscode-styled-components&lt;/a&gt; extension, which will give you syntax highlighting and CSS code completion when working with styled-components.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those funky back-ticks above are a new(ish) JavaScript feature called &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals"&gt;Tagged Template Literals&lt;/a&gt;. You've used and loved normal template literals (&lt;code&gt;Hello ${name}!&lt;/code&gt;) before. Tagged Template Literals are the same, but instead of doing the default string interpolation function when you use them on their own, you get to defined the function that gets ran on the string. Above, &lt;code&gt;styled.button&lt;/code&gt; is a function that will take in the text in the literal and manipulate it into a React component with a class attached to it with our CSS. In the end, the &lt;code&gt;MyButton&lt;/code&gt; component will emit HTML that will look similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-bdnylx bIGghp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click Me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;SC will worry about generating a unique class name on your behalf so your CSS is scoped to your component, and it won't affect other styles in your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Installing SC is a breeze. To get it into your Ionic React project run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i styled-components @types/styled-components
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will install SC and it's typings so we can get great TypeScript autocompletion and type checking.&lt;/p&gt;

&lt;p&gt;Next, import the styled object directly from &lt;code&gt;styled-components&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;styled-components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then start creating styled-components using the &lt;code&gt;styled&lt;/code&gt; object. In the first example above, we created a button. However, the styled object has all the basic HTML primitive tags on it, like &lt;code&gt;div&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;header&lt;/code&gt;, you name it. You can also extend other React components, as long as the component takes in &lt;code&gt;className&lt;/code&gt; as a prop, and that className gets applied to the underlying HTML element the component renders. &lt;/p&gt;

&lt;h2&gt;
  
  
  Scoping CSS to an IonPage
&lt;/h2&gt;

&lt;p&gt;One of the big benefits you get when using Ionic Angular is that the styles you define using the &lt;code&gt;:host&lt;/code&gt; pseudo-selector in a component's CSS file are scoped only to that component. You can achieve the same thing in Ionic React using SC. Below, we will create a "styled" &lt;code&gt;IonPage&lt;/code&gt; by passing in &lt;code&gt;IonPage&lt;/code&gt; into the styled object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;IonPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;`

`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And replacing &lt;code&gt;IonPage&lt;/code&gt; with &lt;code&gt;MyPage&lt;/code&gt; in the JSX:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyPage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonHeader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonHeader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonContent&lt;/span&gt; &lt;span class="na"&gt;fullscreen&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonContent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;MyPage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Adding styles directly to &lt;code&gt;IonPage&lt;/code&gt; isn't terribly useful, though. What it does buy us, however, is we can now target other classes in our page without fear that they will affect another page. Styled Components supports nesting CSS classes, a feature that I loved in SASS and find it hard to live without in plain CSS (though we might someday get &lt;a href="https://github.com/w3c/csswg-drafts/issues/2701"&gt;native nesting support&lt;/a&gt; in CSS). So, we can start to define new elements with classes and target them in our &lt;code&gt;MyPage&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;IonPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;`
  .box {
    border: 1px solid white;
    margin: 10px;
    height: 200px;
    width: 200px;
  } 
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyPage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonHeader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonHeader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonContent&lt;/span&gt; &lt;span class="na"&gt;fullscreen&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"box"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonContent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;MyPage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please forgive my lack of design powers, I rolled a 2 on Aesthetics when born.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Styling an Ionic Component
&lt;/h2&gt;

&lt;p&gt;Styling one of the built-in Ionic components couldn't be easier. In fact, you already saw how to do so in the above example. Let's look at a component that offers some more options though, like &lt;code&gt;IonButton&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;IonButton&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;`
  --background: green;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// And to use it:&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyButton&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;MyButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ionic components are web components that use Shadow DOM, therefore, styles defined outside of the web component don't affect the inner parts of the web component. To get around this, Ionic uses CSS Variables that allow developers to set styles that the web component can use.&lt;/p&gt;

&lt;p&gt;In the above example, we set the &lt;code&gt;background&lt;/code&gt; and &lt;code&gt;border-color&lt;/code&gt; CSS variables, and these changes will only apply to our &lt;code&gt;MyButton&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;Recently, Ionic also released support for Shadow Parts, which allows web component developers to expose certain parts of the web component and have those parts be targeted via CSS. Below, we accomplish the same thing as the example above but we target the native HTML button via the &lt;code&gt;native&lt;/code&gt; shadow part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;IonButton&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;amp;::part(native) {
    background-color: green;
  }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;amp;&lt;/code&gt; refers to the parent component itself, then we use the &lt;code&gt;part&lt;/code&gt; pseudo-selector and target the &lt;code&gt;native&lt;/code&gt; shadow part that the &lt;code&gt;IonButton&lt;/code&gt; exposes.&lt;/p&gt;

&lt;p&gt;Learn more about &lt;a href="https://ionicframework.com/docs/theming/css-variables"&gt;CSS Variables&lt;/a&gt; and &lt;a href="https://ionicframework.com/docs/theming/css-shadow-parts"&gt;Shadow Parts&lt;/a&gt; from our docs, and check out this post about &lt;a href="https://ionicframework.com/blog/customize-your-ionic-framework-app-with-css-shadow-parts/"&gt;Shadow Parts&lt;/a&gt; on the Ionic blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;CSS in React, (and Ionic React!), is a tricky subject, but fortunately, there are many ways you can go about it.&lt;/p&gt;

&lt;p&gt;Hopefully, I gave you a taste of what you can do by using styled-components in an Ionic React app and opened your eyes to some of the possibilities. &lt;/p&gt;

&lt;p&gt;I just covered the bare minimum of what you can do with styled-components, there's much more to it, and once you get the grasp of the library, you can create some pretty powerful components.  I encourage you to visit the &lt;a href="https://styled-components.com/docs"&gt;Styled Component docs&lt;/a&gt; to learn more and give it a try.&lt;/p&gt;

&lt;p&gt;How are you going about styling your Ionic React apps? Is there something more you want to see? Hit me up in the comments below for reach out to me &lt;a href="https://twitter.com/elylucas"&gt;@elylucas&lt;/a&gt; and I just might write about it.&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

</description>
      <category>ionic</category>
      <category>react</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Testing Ionic React Apps with Jest and React Testing Library</title>
      <dc:creator>Ely Lucas</dc:creator>
      <pubDate>Tue, 18 Feb 2020 16:42:33 +0000</pubDate>
      <link>https://dev.to/ionic/testing-ionic-react-apps-with-jest-and-react-testing-library-b2b</link>
      <guid>https://dev.to/ionic/testing-ionic-react-apps-with-jest-and-react-testing-library-b2b</guid>
      <description>&lt;p&gt;It's 2020, and the testing frameworks for JavaScript applications have improved dramatically over the past few years. &lt;/p&gt;

&lt;p&gt;Thanks to tools like Jest and React Testing Library, you can be testing your apps with a few lines of code.&lt;/p&gt;

&lt;p&gt;Have you been looking into testing an Ionic React project, but not sure where to start? In this post, I'll go over the basics of how to get started as we build out an app using tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools
&lt;/h2&gt;

&lt;p&gt;First, let us go over a few of the tools that we will be using.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; is a testing framework built by the teams at Facebook (like React) and is very similar to other test frameworks like Jasmine and Mocha. Jest has been the defacto standard in React testing for quite awhile and is gaining popularity in other communities as well. What makes Jest great is it is easy to use, is flexible in the types of tests you want to create, and has a powerful test runner that is smart about running only tests for code that has changed.&lt;/p&gt;

&lt;p&gt;Jest is already included and set up when you create a new Ionic React project, so getting started is super easy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/docs/react-testing-library/intro" rel="noopener noreferrer"&gt;React Testing Library&lt;/a&gt; (RTL from here on out) is a relative newcomer to the React landscape, but it is also grown immensely in popularity. RTL lets you test React components without relying on the internal implementation details of the component. This approach mimics more of the way an actual user would use the app and promises to make tests more reliable and less brittle to change.&lt;/p&gt;

&lt;p&gt;React Testing Library is now included when creating a new Ionic App and is our recommended test library.&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://github.com/ionic-team/ionic-react-test-utils" rel="noopener noreferrer"&gt;Ionic React Test Utils&lt;/a&gt; is a small suite of utilities that can help when testing Ionic React apps. It includes helpers to fire off custom Ionic events and mocks around some of our more complex components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Our demo will keep a list of things we need to get done (don't call it a todo app!). Okay, it's a todo app, but a todo app is basic enough yet covers a few of the points I want to hit on testing Ionic components.&lt;/p&gt;

&lt;p&gt;Start off creating a New Ionic React project via the Ionic CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;ionic start ionic-react-todos blank &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;react  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you are new to Ionic and need to get things set up, view our getting started guide &lt;a href="https://ionicframework.com/docs/react/your-first-app" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This command kicks off a new Ionic React project named "react-todos"  using the blank template.&lt;/p&gt;

&lt;p&gt;Next, we need to install Ionic React Test Utils, which we will use a bit later on. Go into the new directory and install the npm package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;ionic-react-todos
npm i @ionic/react-test-utils
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open up the project in your code editor. If you look in the &lt;code&gt;src&lt;/code&gt; folder, you might notice that we already have a test file created for us in &lt;code&gt;App.test.tsx&lt;/code&gt;. It is a simple test that just makes sure the main app component renders correctly without throwing an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;renders without crashing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unmountComponentAtNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Go back into your command line, and fire up the Jest test runner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fSan_myc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2020/02/jest-no-test.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fSan_myc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2020/02/jest-no-test.jpg" alt="" class="aligncenter size-full wp-image-3167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might get a message saying there were no tests found, which is because Jest (by default) only runs tests on files that are modified since the last git commit. This is pretty handy and helps speed up testing by only running tests on files you are currently working on. Fortunately, we can see on the menu that we can change the "Watch Usage". Press "a" to run all tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CbCRJBFp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2020/02/jest-pass.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CbCRJBFp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2020/02/jest-pass.jpg" alt="" class="aligncenter size-full wp-image-3168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go ahead and leave Jest running. If we make any changes to our code, Jest automatically reruns the tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your First Test
&lt;/h2&gt;

&lt;p&gt;Okay, back in the code, let's create a test at &lt;code&gt;src/pages/Home.test.tsx&lt;/code&gt;. We will create a basic test that makes sure the title of our page is "Ionic React Todos". Paste the following code into the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@testing-library/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;page should have a title of Ionic React Todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;findByText&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ionic React Todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's break down the basic anatomy of a test. First, we have our imports, including the &lt;code&gt;render&lt;/code&gt; method from RTL and then our &lt;code&gt;Home&lt;/code&gt; component. Then we have our actual test. The &lt;code&gt;test&lt;/code&gt; method is from Jest and is available globally, so there is no need to import it. For its first parameter, it takes in the name of the test, in which we usually provide some detailed text about what we are trying to accomplish, and then an anonymous function that contains the code for the test.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;render&lt;/code&gt; method takes our component and returns a bunch of helper methods to aid us in selecting pieces of the DOM that was rendered. The &lt;code&gt;findByText&lt;/code&gt; method is one of them, and it looks for an element that contains the text passed into it. If it doesn't find one (or finds more than one), &lt;code&gt;findByText&lt;/code&gt; throws an error. Therefore, there is no need for us to test the return value of &lt;code&gt;findByText&lt;/code&gt; in this case.&lt;/p&gt;

&lt;p&gt;For a list of all the helper methods &lt;code&gt;render&lt;/code&gt; returns, check out the RTL docs &lt;a href="https://testing-library.com/docs/dom-testing-library/api-queries#queries" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you view Jest again you will see that the test failed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wMe6ODKh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2020/02/jest-failed-test.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wMe6ODKh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2020/02/jest-failed-test.jpg" alt="" class="aligncenter size-full wp-image-3169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We get a descriptive output about what happened and where. But basically, our text "Ionic React Todos" wasn't found. Update the &lt;code&gt;&amp;lt;IonTitle&amp;gt;&lt;/code&gt; text in the Home component and come back, the tests should now pass:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o5uM1ui1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2020/02/jest-passed-test.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o5uM1ui1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2020/02/jest-passed-test.jpg" alt="" class="aligncenter size-full wp-image-3170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that's what we like to see!&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Todo List
&lt;/h2&gt;

&lt;p&gt;We have our first test passing, so that means it is time to write another failing one! When we have no todos to display, we want a message that says there are none. Here is the test for that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when there are no todos, a no todos message should show&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;findByText&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No todos, add some!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Take a look at the Jest runner, and you should see the new test failing. Let's update the component to display the message when there are no todos.&lt;/p&gt;

&lt;p&gt;To get started, add a &lt;code&gt;Todo&lt;/code&gt; interface to the top of &lt;code&gt;Home&lt;/code&gt; and create a state variable using the &lt;code&gt;useState&lt;/code&gt; hook to hold the todos. Also, update the &lt;code&gt;&amp;lt;IonContent&amp;gt;&lt;/code&gt; to display a message if there are no todos.&lt;/p&gt;

&lt;p&gt;Update &lt;code&gt;Home&lt;/code&gt; to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonPage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonHeader&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonToolbar&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonTitle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Ionic&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="nx"&gt;Todos&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonTitle&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonToolbar&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonHeader&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonContent&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ion-padding&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;No&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="nx"&gt;some&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonContent&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonPage&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our last test should now be passing. Let us write another to make sure our todos appear when there are some:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when TodoList is loaded with todos, then the todos should be in the list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;review PR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update docs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;findByText&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are running a couple of &lt;code&gt;findByText&lt;/code&gt; calls here to make sure both the todos are added. If either of these fails to find an element, then an error is thrown.&lt;/p&gt;

&lt;p&gt;Next, replace the &lt;code&gt;&amp;lt;div&amp;gt;todos will go here&amp;lt;/div&amp;gt;&lt;/code&gt; placeholder with this snippet which creates an &lt;code&gt;IonList&lt;/code&gt; with an &lt;code&gt;IonItem&lt;/code&gt; for each of the todos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonList&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonItem&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonLabel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonLabel&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonIcon&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;trash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;trash&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;danger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;slot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonItem&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;))}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonList&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;All the Ionic components are imported from &lt;code&gt;@ionic/react&lt;/code&gt;, and the &lt;code&gt;trash&lt;/code&gt; icon is imported from &lt;code&gt;ionicons/icons&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But wait, how's our &lt;code&gt;Home&lt;/code&gt; component getting the array of Todos? Right now, it's not. Let's pretend the &lt;code&gt;Home&lt;/code&gt; component calls into an API to fetch the todos. We won't have a real API, but we will create and load a json file with some data. Create a file at &lt;code&gt;public/assets/todos.json&lt;/code&gt; and paste the following into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"review PR"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update readme"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"write docs"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Back in &lt;code&gt;Home&lt;/code&gt;, add a &lt;code&gt;useEffect&lt;/code&gt; hook to call into the API and set the todos state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;doFetch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/assets/todos.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;doFetch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Mocking an HTTP Request
&lt;/h2&gt;

&lt;p&gt;Our tests start to fail because the fetch call won't be able to make the request while running in Jest. Fortunately, Jest allows us to mock fetch and return specific data. Add the following &lt;code&gt;mockFetch&lt;/code&gt; method to the test file, which allows us to pass in some data that returns from the &lt;code&gt;fetch&lt;/code&gt; call, as well as the &lt;code&gt;beforeEach&lt;/code&gt; Jest helper, which calls the mock function before each test runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;mockFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;mockResolvedValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mockFetch&lt;/span&gt;&lt;span class="p"&gt;([]));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the last test, we can call &lt;code&gt;mockFetch&lt;/code&gt; and pass in our todo array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// todos array&lt;/span&gt;
&lt;span class="nx"&gt;mockFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// render and fetchByText methods&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When the component calls &lt;code&gt;fetch&lt;/code&gt;, it now returns the mock test data we have set up for it, and our test passes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a Todo
&lt;/h2&gt;

&lt;p&gt;Next up is the bulk of our functionality, adding a todo to the list!&lt;/p&gt;

&lt;p&gt;The next test does quite a bit. We will test clicking an add todo button, verifying the todo from loads, filling out the form, submitting the form, and lastly, making sure the todo appears in the list. This might seem like a lot, but RTL promotes testing a page, much like how a user would interact with it. This lets us cover quite a bit of functionality in a single test.&lt;/p&gt;

&lt;p&gt;Go ahead and start to stub out the test with our first step: clicking a button to display the new todo form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when clicking the new button, we should be able to add a new todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;findByTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;findByText&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;findByTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Add Todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
  &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;fireEvent&lt;/code&gt; is imported from &lt;code&gt;@testing-library/react&lt;/code&gt; and helps us simulate user interaction with the DOM elements that get returned. Here we are using it to click the &lt;code&gt;addButton&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are also using a new function returned from &lt;code&gt;render&lt;/code&gt; here, &lt;code&gt;findByTitle&lt;/code&gt;, which works very similarly to &lt;code&gt;findByText&lt;/code&gt;, but instead looks for an element for a certain title. Update the &lt;code&gt;Home&lt;/code&gt; component and add the following right above the closing &lt;code&gt;&amp;lt;/IonContent&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonFab&lt;/span&gt; &lt;span class="nx"&gt;vertical&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bottom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;horizontal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonFabButton&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Add Todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setShowModal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonIcon&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;add&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonFabButton&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonFab&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonModal&lt;/span&gt;
  &lt;span class="nx"&gt;onDidDismiss&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setShowModal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;showModal&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Todo Form will go here */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonModal&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Also add the state variable (right below the todos state) to maintain if we are displaying the modal containing the todo form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;showModal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setShowModal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Buh uh oh, the tests have started to fail due to a new error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QNXsc9NY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2020/02/jest-modal-fail.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QNXsc9NY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2020/02/jest-modal-fail.jpg" alt="" class="aligncenter size-full wp-image-3171"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This error leads us up to a bit of a tricky spot when it comes to testing Ionic React. &lt;/p&gt;

&lt;h2&gt;
  
  
  Using Ionic React Test Utils to mock Ionic Web Components
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Right before this blog was published, the JSDOM team &lt;a href="https://github.com/jsdom/jsdom/blob/master/Changelog.md#1620" rel="noopener noreferrer"&gt;announced&lt;/a&gt; customElement support for JSDOM. We are excited for this and will monitor if it fixes the issues we ran into and will update the post accordingly. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ionic is written in web components, and Ionic React is a thin layer around those components to make them feel and behave more like the React components. Unfortunately, JSDOM does not currently support web components and errors if trying to access web component API, like &lt;code&gt;customElements&lt;/code&gt; in the error above.&lt;/p&gt;

&lt;p&gt;What we can do here, though, is use Jest to mock out the Ionic React components that can't render (like IonModal), which is where Ionic React Test Utils (IRTU) comes into play. IRTU provides a helper that can mock out all known Ionic components that have issues rendering in JSDOM. The mocked components simulate the actual ones by rendering minimal DOM to test.&lt;/p&gt;

&lt;p&gt;To set it up, go into &lt;code&gt;src/setupTests.ts&lt;/code&gt; file and update it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mockIonicReact&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react-test-utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;mockIonicReact&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That should get the test passing. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For more info around this method, read &lt;a href="https://dev.to/ionic/testing-web-components-in-react-4e49" rel="noopener noreferrer"&gt;Testing Web Components in React&lt;/a&gt; on Dev.to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Testing the Form
&lt;/h2&gt;

&lt;p&gt;Ok, let's continue flushing this test out. Now that the modal is loading, we will get the input box and save button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;findByTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Todo Text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Save&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Time to implement the functionality for the form. Add the following form to the &lt;code&gt;IonModal&lt;/code&gt; in &lt;code&gt;Home&lt;/code&gt;, replacing &lt;code&gt;{/* Todo Form will go here */}&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonToolbar&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonTitle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonTitle&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonToolbar&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonContent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonList&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonItem&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonLabel&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stacked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonLabel&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonInput&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Todo Text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;onIonChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonItem&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonList&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonButton&lt;/span&gt; &lt;span class="nx"&gt;expand&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;Save&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonButton&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonContent&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And add the new text/setText state variables as well as the method to save the todos to the top of the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nextId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;text&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;setTodos&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;setShowModal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, we need to simulate filling out the form and clicking the save button. Typically, you would use the &lt;code&gt;fireEvent.change&lt;/code&gt; method from RTU to simulate an input change. This fires the input element's &lt;code&gt;change&lt;/code&gt; event. However, Ionic components fire custom 'ion' events like 'ionChange'. Therefore, we can't use &lt;code&gt;fireEvent.change&lt;/code&gt; here.&lt;/p&gt;

&lt;p&gt;To help with this, IRTU exports &lt;code&gt;ionFireEvent&lt;/code&gt;, which wraps RTU's &lt;code&gt;fireEvent&lt;/code&gt;, and augments it with all the custom Ionic events. Therefore, you can use &lt;code&gt;ionFireEvent&lt;/code&gt; as a drop in replacement for &lt;code&gt;fireEvent&lt;/code&gt;. To do so, import &lt;code&gt;ionFireEvent&lt;/code&gt; and alias it to &lt;code&gt;fireEvent&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ionFireEvent&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fireEvent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react-test-utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And remove the &lt;code&gt;fireEvent&lt;/code&gt; import from &lt;code&gt;@testing-library/react&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, we fire the &lt;code&gt;ionChange&lt;/code&gt; event and click the button and verify that our todo gets added to the list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ionChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  How did we do?
&lt;/h2&gt;

&lt;p&gt;So far, we have written quite a bit of functionality driven entirely by tests. If we fire up the dev server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;ionic serve 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We should see the list of todos get loaded from the fetch request, and be able to create a new todo.&lt;/p&gt;

&lt;p&gt;You might notice there are delete icons for each of the todos. I'll leave that as an exercise for you to implement the delete functionality. &lt;/p&gt;

&lt;p&gt;I've included a link to the full demo repository below that expands on the app a bit more, including deleting todos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;It might have taken a bit of extra time to drive this development through tests, but now we have a good set of tests to run anytime we make updates to the app that gives us confidence that we didn't break anything.&lt;/p&gt;

&lt;p&gt;Here is a list of resources for more info about the content covered in this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://testing-library.com/docs/react-testing-library/intro" rel="noopener noreferrer"&gt;React Testing Library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/ionic-team/ionic-react-test-utils" rel="noopener noreferrer"&gt;Ionic React Test Utils&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kentcdodds.com/" rel="noopener noreferrer"&gt;Kent C Dodd's Blog&lt;/a&gt; (author of React Testing Library and great articles on testing)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://github.com/elylucas/ionic-react-todos" rel="noopener noreferrer"&gt;Github repo for the demo app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Interested in learning more about testing Ionic React apps? Let us know down below with your questions or comments.&lt;/p&gt;

&lt;p&gt;Until next time, may your tests be green and your todos list complete!&lt;/p&gt;

&lt;p&gt;Happy coding.&lt;/p&gt;

</description>
      <category>react</category>
      <category>ionic</category>
      <category>tutorial</category>
      <category>testing</category>
    </item>
    <item>
      <title>Testing Web Components in React?</title>
      <dc:creator>Ely Lucas</dc:creator>
      <pubDate>Thu, 30 Jan 2020 22:33:50 +0000</pubDate>
      <link>https://dev.to/ionic/testing-web-components-in-react-4e49</link>
      <guid>https://dev.to/ionic/testing-web-components-in-react-4e49</guid>
      <description>&lt;p&gt;Testing in React is a joy. Between Jest and React Testing Library (RTL from here on out), it is super simple to get up and running and productive. In fact, as of the CRA 3 release, RTL is included out of the box, making it even quicker.&lt;/p&gt;

&lt;p&gt;However, there has been a sticky point that got me hung up lately, and that's been around testing a React component that consumes web components.&lt;/p&gt;

&lt;p&gt;In this post, I'm going to cover the problem that arises with web components and testing, and show a particular method I used to get around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Sticky Point
&lt;/h2&gt;

&lt;p&gt;At &lt;a href="https://ionicframework.com/"&gt;Ionic&lt;/a&gt;, all of our UI components are written as web components via &lt;a href="https://stenciljs.com/"&gt;StencilJS&lt;/a&gt;. In Ionic React, we wrap those web components with lite React wrappers to help wire up the events and provide a better developer experience. Therefore, using an Ionic React component like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonLabel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonLabel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Essentially becomes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ion-label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sc-ion-label-ios-h sc-ion-label-ios-s ios hydrated"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello&lt;span class="nt"&gt;&amp;lt;/ion-label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By the time it is rendered in the browser.&lt;/p&gt;

&lt;p&gt;There's quite a bit of logic in an Ionic component.&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;&amp;lt;ion-label&amp;gt;&lt;/code&gt; is one of the simplest components we have.&lt;/p&gt;

&lt;p&gt;I've been working on some testing guidance for Ionic React apps, and in doing so, I found that testing web components has its own set of challenges.&lt;/p&gt;

&lt;p&gt;Now, this isn't a problem with Ionic, React, Jest, or RTL. The problem lies that the engine that Jest uses by default to render React components is JSDOM, which doesn't support web components at this time.&lt;/p&gt;

&lt;p&gt;Therefore, when rendered, our &lt;code&gt;&amp;lt;IonLabel&amp;gt;&lt;/code&gt; component example above, appears like this to RTL when JSDOM is done rendering it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ion-label&amp;gt;&lt;/span&gt;Hello&lt;span class="nt"&gt;&amp;lt;/ion-label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It is an empty HTML tag with no style, logic, or functionality. Not at all the same thing that gets rendered in the browser.&lt;/p&gt;

&lt;p&gt;At first, I was determined to get these web components up and running so writing tests in RTL would match what gets rendered in a browser as closely as possible. I did have some requirements, however. I did not want to have to export the React app or have to do some overly complex configuration to get it running.&lt;/p&gt;

&lt;p&gt;So, I tried a few different polyfills and some different Jest environments that support web components, but never quite got anything to work.&lt;/p&gt;

&lt;p&gt;So, how are we supposed to test a React app that relies on web components if the web components don't render?&lt;/p&gt;

&lt;p&gt;After thinking on it for a bit, however, I realized that maybe web components don't need to work to test &lt;em&gt;your&lt;/em&gt; React app.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is it you are trying to test?
&lt;/h2&gt;

&lt;p&gt;When writing tests, you are often testing that some effect you induce to your component has some result. You load a page, and data appears. You click a button, and an XHR request gets sent off. An input gets changed, and updates a label elsewhere on the page, etc...&lt;/p&gt;

&lt;p&gt;None of these have much to do with the internal workings of a web component. &lt;/p&gt;

&lt;p&gt;When I have a list of items that I want to render into a &lt;code&gt;&amp;lt;IonList&amp;gt;&lt;/code&gt;, I don't need to see all the divs, header tags, slots, and shadow dom created. All I want is to see the items I expect to appear in the list. &lt;/p&gt;

&lt;p&gt;And, this is precisely what I get, even if the web components aren't working.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonList&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;people&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonItem&lt;/span&gt;&lt;span class="err"&gt;)}&lt;/span&gt;
&lt;span class="err"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="na"&gt;IonList&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Renders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ion-list&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ion-item&amp;gt;&lt;/span&gt;Ely&lt;span class="nt"&gt;&amp;lt;/ion-item&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ion-item&amp;gt;&lt;/span&gt;Joe&lt;span class="nt"&gt;&amp;lt;/ion-item&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ion-list&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I can use RTL to make sure all my peeps are in that list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await waitForElement(() =&amp;gt; people.map(p =&amp;gt; getByText(p.name));
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;None of the test logic changes because my web component didn't render.&lt;/p&gt;

&lt;p&gt;The empty, lifeless HTML tags that get rendered still respond to  DOM events, too, so you can simulate a click on a &lt;code&gt;&amp;lt;IonButton&amp;gt;&lt;/code&gt;. The buttons &lt;code&gt;onClick&lt;/code&gt; event still triggers, and from our tests perspective, it doesn't matter if the event came from the HTML tag or the internal workings of IonButton.&lt;/p&gt;

&lt;p&gt;What this comes down to is that we can think of a web component as an external piece of code, and the implementation details of that code shouldn't affect our app. &lt;/p&gt;

&lt;h2&gt;
  
  
  Testing components with Logic
&lt;/h2&gt;

&lt;p&gt;But what if a web component has logic in it that we need to happen for our app to work? For instance, having a modal open when setting a prop, or having a toggle component fire an onChange event when clicked?&lt;/p&gt;

&lt;p&gt;Not having logic work in Ionic components was a tricky sticky point for me and what caused me to search for so long on how to get web components to work with Jest and JSDOM.&lt;/p&gt;

&lt;p&gt;After a bit though, I came to another realization: A web component can be thought of like an external service, and it might be possible to mock it as such.&lt;/p&gt;

&lt;p&gt;If you are using web components in React, then the chances are good that you are using React wrappers around your web components to access the web components props and events (much like Ionic React wraps the core Ionic web components).&lt;/p&gt;

&lt;p&gt;If you do use wrappers, then you can mock the React components you import via Jest's excellent mocking capabilities. If you don't and you use raw web components in your JSX, well, I'm not sure then. Maybe it's a good time to wrap them?&lt;/p&gt;

&lt;p&gt;Let's say you have a toggle web component that fires a custom change event when clicked. Since the change event is raised internally in the component, it won't fire when running under JSDOM. Bummer! However, if we mock out that component, we can fire the event ourselves in the mock. &lt;/p&gt;

&lt;p&gt;Here is an example of a web component, a React component the wraps the web one, the test, and the mock:&lt;/p&gt;

&lt;p&gt;A web component defined somewhere in your app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;window.customElements.define('my-toggle', class extends HTMLElement {
  checked = false;
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });
    const toggle = document.createElement('input');
    toggle.setAttribute('type', 'checkbox');
    toggle.addEventListener('click', (e: MouseEvent) =&amp;gt; {
      this.checked = !this.checked;
      this.dispatchEvent(new CustomEvent('myChange', {
        detail: {
          checked: this.checked
        }
      }));
    });
    shadow.appendChild(toggle);
  }
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The React wrapper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;MyToggleProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;onMyChange&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyToggle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyToggleProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;onMyChange&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;onMyChange&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myChange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;onMyChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;my&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;toggle&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;my&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;MyToggle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And here how we could use the wrapper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyToggle&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;testid&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-toggle"&lt;/span&gt; &lt;span class="na"&gt;onMyChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setShowText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;MyToggle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;showText&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;More text!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We will set up our test as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when my toggle is clicked it should show more text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;getByTestId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getByText&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-toggle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;waitForElement&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;More text!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;However, when the test runs, the div element won't show up when the toggle is clicked. Why? Because the web component isn't actually running right now, and therefore the custom event firing in the click handler is not either.&lt;/p&gt;

&lt;p&gt;So, I propose to mock the MyToggle React component with a slim implementation of the web component functionality.&lt;/p&gt;

&lt;p&gt;To do so, add this mock somewhere in the setup of your tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;mockMyToggle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;onMyChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;my&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;onMyChange&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;onMyChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myChange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;my&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../components/MyToggle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mockMyToggle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;jest.mock&lt;/code&gt; method mocks out the entire MyToggle module at that specific path, which is where our component imports it from.  &lt;a href="https://jestjs.io/docs/en/mock-functions#mocking-modules"&gt;More info&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this mock, we have set up enough logic to simulate our web component without actually using it.&lt;/p&gt;

&lt;p&gt;Now, when our test runs, it uses the mock instead of the real React component, and our test passes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thoughts?
&lt;/h2&gt;

&lt;p&gt;The above is a lot to digest, but my premise is that if you are having issues trying to get web components to work in JSDOM, maybe you don't need to.&lt;/p&gt;

&lt;p&gt;I haven't taken this idea too far yet, but so far, it has worked out decently. I'd love to hear your thoughts about the above approach or if you had success with other methods.&lt;/p&gt;

&lt;p&gt;And, who knows, maybe JSDOM will land web component support, and this will all be moot.&lt;/p&gt;

</description>
      <category>react</category>
      <category>testing</category>
      <category>ionic</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Announcing Ionic React Hooks</title>
      <dc:creator>Ely Lucas</dc:creator>
      <pubDate>Tue, 03 Dec 2019 17:06:50 +0000</pubDate>
      <link>https://dev.to/ionic/announcing-ionic-react-hooks-49ol</link>
      <guid>https://dev.to/ionic/announcing-ionic-react-hooks-49ol</guid>
      <description>&lt;p&gt;Last month we &lt;a href="https://ionicframework.com/blog/announcing-ionic-react/"&gt;announced Ionic React&lt;/a&gt;, and have been blown away by the reception from both the Ionic community as well as the React community. Today, we are excited to launch a companion project to Ionic React that makes tying into device hardware and APIs a breeze in an Ionic React project. &lt;/p&gt;

&lt;p&gt;You may have heard of &lt;a href="http://capacitor.ionicframework.com/" rel="noopener noreferrer"&gt;Capacitor&lt;/a&gt;, our native app management layer that lets you leverage APIs that work across iOS, Android, Electron, and the web, all with one code base and JS. You can use Capacitor to access various device features, such as the camera, GPS, network status, and more. The SDK for Capacitor is vanilla JavaScript, which any web framework can use. However, we wanted to take the experience of accessing Capacitor APIs to the next level by making them feel like a natural extension to the React experience.&lt;/p&gt;

&lt;p&gt;So today, we are launching &lt;a href="" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/ionic-team/ionic-react-hooks"&gt;Ionic React Hooks&lt;/a&gt;, a collection of React hooks that act as wrappers around the Capacitor APIs, as well as some other Ionic platform-specific features. With Ionic React Hooks, you can start accessing device hardware in just a few lines of code, all while coding in a familiar React paradigm.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the Hook for Hooks?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://reactjs.org/docs/hooks-intro.html"&gt;Hooks&lt;/a&gt; were introduced in React v16.8 and allow a way to access certain React features in a simple and clean manner while using functional components. &lt;/p&gt;

&lt;p&gt;Before hooks, you pretty much needed to use class-based components to have local state, make web API calls, use React's context, or tie into a component's lifecycle events. While class-based components are great, developers often prefer to use the functional approach, which consists of a simple function that takes in a props object and returns a React element. Functional components are often smaller and easier to understand than their class-based counterparts.&lt;/p&gt;

&lt;p&gt;Hooks make it possible to do more complex tasks in a functional component.&lt;/p&gt;

&lt;p&gt;For more information about React Hooks and a primer on how they work, head over to React's guide to hooks &lt;a href="https://reactjs.org/docs/hooks-intro.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At Ionic, we are fans of functional components and hooks, so it was natural to want a way to consume the APIs we build using a hooks based approach. And that's where Ionic React Hooks comes in! Let's see how to get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Example
&lt;/h2&gt;

&lt;p&gt;Hooks are great because they abstract away the messy details of accessing Capacitor APIs and setting/clearing listeners. For example, to track Geolocation position in realtime, we just need to call one hook in our component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useWatchPosition&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react-hooks/geolocation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currentPosition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startWatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clearWatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useWatchPosition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And &lt;code&gt;currentPosition&lt;/code&gt; will be available in our component to access. The return type of the hook follows the &lt;a href="https://capacitor.ionicframework.com/docs/apis/geolocation"&gt;Geolocation API&lt;/a&gt; in Capacitor, but all the events and handlers are managed for you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Ionic React Hooks to an existing Ionic React Project
&lt;/h2&gt;

&lt;p&gt;For those using Ionic React today, adding Ionic React Hooks to your project is simple. First, enable Capacitor in your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;ionic integrations &lt;span class="nb"&gt;enable &lt;/span&gt;capacitor
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, install Ionic React Hooks and PWA Elements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @ionic/pwa-elements @ionic/react-hooks
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Ionic React Hooks Tutorial
&lt;/h2&gt;

&lt;p&gt;Let's build a simple camera app that takes a picture and displays it on the user’s device. The best part? It will run on the web or as a native mobile app - with no code changes - thanks to Capacitor.&lt;/p&gt;

&lt;p&gt;First, let's kick off a new Ionic React application through the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;ionic start my-react-app blank &lt;span class="nt"&gt;--type&lt;/span&gt; react
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you are new to Ionic or need to get the CLI installed, visit &lt;a href="https://ionicframework.com/docs/installation/cli" rel="noopener noreferrer"&gt;here&lt;/a&gt; for info on how to get started with the Ionic CLI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, enable Capacitor in the project by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;ionic integrations &lt;span class="nb"&gt;enable &lt;/span&gt;capacitor
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will prepare the project by installing the necessary Capacitor dependencies and setting up some configuration for us.&lt;/p&gt;

&lt;p&gt;Now install the Ionic PWA Elements and Ionic React Hooks packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i @ionic/pwa-elements @ionic/react-hooks
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;PWA elements is a set of &lt;a href="Stencil" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://stenciljs.com/"&gt;https://stenciljs.com/&lt;/a&gt; web components that display UI elements when not running natively on a device. These elements are lazy-loaded as needed, so including them will not increase your initial bundle size. In our demo app, the camera interface displayed is from PWA Elements.&lt;/p&gt;

&lt;p&gt;Note: PWA Elements is optional, the hooks will work without it but provide a simpler UI experience on the web.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have the project created and all our dependencies set up, open the project in your favorite code editor.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;index.tsx&lt;/code&gt;, we need to register the PWA Elements library. Update the file to import &lt;code&gt;defineCustomElements&lt;/code&gt; and call that method at the bottom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineCustomElements&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/pwa-elements/loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;defineCustomElements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, open up &lt;code&gt;Home.tsx&lt;/code&gt;, and add the following code right below the imports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCamera&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;availableFeatures&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react-hooks/camera&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You import each of the hooks for a specific Capacitor plugin by importing them from their specific path.&lt;/p&gt;

&lt;p&gt;Each of the plugins also has an &lt;code&gt;availableFeatures&lt;/code&gt; object. While Capacitor allows you to write to one API across several platforms, not all features are supported on all platforms. It is recommended to check if the feature you intend to use is available before using it to avoid any runtime errors.&lt;/p&gt;

&lt;p&gt;Inside the functional component, add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getPhoto&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCamera&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleTakePhoto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;availableFeatures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getPhoto&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;getPhoto&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;allowEditing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;resultType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CameraResultType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DataUrl&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;CameraResultType is imported from "@capacitor/core"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From the &lt;code&gt;useCamera&lt;/code&gt; hook, we get back two values. The first is the &lt;code&gt;photo&lt;/code&gt; value, a &lt;code&gt;CameraPhoto&lt;/code&gt; object which contains meta-data around the result of the method call. It will be undefined at first. However, it will be updated with the result of &lt;code&gt;getPhoto&lt;/code&gt; when that method is called (similar to how the state variable from &lt;code&gt;useState&lt;/code&gt; works). The &lt;code&gt;getPhoto&lt;/code&gt; method, when invoked, will launch the camera plugin to take a photo.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;handleTakePhoto&lt;/code&gt; method will be invoked from a button click later, but here we are simply checking if the platform the app is currently running on can get a photo, and then calling into the &lt;code&gt;getPhoto&lt;/code&gt; method with some options.&lt;/p&gt;

&lt;p&gt;Next, replace the content of &lt;code&gt;IonContent&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;availableFeatures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getPhoto&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonButton&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleTakePhoto&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Take Photo&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataUrl&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Camera not available on this platform&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here, we check if the feature is once again available (so we don't show a button that can’t be used), and if so, display the UI to take the picture and view the photo.&lt;/p&gt;

&lt;p&gt;In the options we send to &lt;code&gt;getPhoto&lt;/code&gt;, we specify dataUrl as the result type. This type gives us back a base64 encoded string of the photo that can be directly set as the image's &lt;code&gt;src&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;With all that in place, we can now run &lt;code&gt;ionic serve&lt;/code&gt;, and from within the web browser, take a photo and display it!&lt;/p&gt;

&lt;p&gt;For more info about how to use the Capacitor Camera API, go to the docs &lt;a href="https://capacitor.ionicframework.com/docs/apis/camera" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running on a Native iOS App
&lt;/h2&gt;

&lt;p&gt;We currently have our app running on one platform - the web - which could be deployed as a PWA and function as is. Now, let's take this app and make a native iOS app and have it run on an iPhone as well.&lt;/p&gt;

&lt;p&gt;First, we need to build our app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This creates a production build of our React app in the &lt;code&gt;build&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Next, add the iOS platform via the Capacitor CLI. This command will create an iOS project for us and copy our build folder into the native project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx cap add ios
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If this is your first time creating a native Capacitor app, you might need to install some additional software. Visit our &lt;a href="https://capacitor.ionicframework.com/docs/ios" rel="noopener noreferrer"&gt;getting started guide&lt;/a&gt; for more info.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once finished, open up the iOS app in Xcode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx cap open ios
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, you can build your app and run it on an actual device!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UEG_2em5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2019/11/ionic-react-hooks-demo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UEG_2em5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2019/11/ionic-react-hooks-demo.jpg" alt="" class="aligncenter size-full wp-image-3029"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For more info on the development workflow in Capacitor, view this &lt;a href="https://capacitor.ionicframework.com/docs/basics/workflow" rel="noopener noreferrer"&gt;doc&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice that when the button is clicked, you get presented with a native iOS UI to either choose a photo from an album or use the camera to take a new photo. Capacitor is automatically detecting you are on an actual device and using that device's capability to offer a better, more natural experience for the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next for Ionic React Hooks?
&lt;/h2&gt;

&lt;p&gt;This first release of Ionic React Hooks is very much a beta release, with only a few of the Capacitor APIs covered so far. &lt;/p&gt;

&lt;p&gt;We are releasing this project as a community-based project and would love to get your feedback and have you help contribute to its future. &lt;/p&gt;

&lt;p&gt;To contribute, head over to the &lt;a href="https://github.com/ionic-team/ionic-react-hooks" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;, file an issue with your idea and then submit a PR. &lt;/p&gt;

&lt;p&gt;Ideas on what to contribute could be bug fixes, new hooks for Capacitor APIs, documentation updates to the readme, or new hooks that would benefit Ionic React developers in general.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ionic-team/ionic-react-hooks"&gt;Ionic React Hooks&lt;/a&gt; aims to make accessing device hardware in Ionic React projects using Capacitor as straightforward as possible for React developers. In the process, they make it easier than using Capacitor by itself or through another framework!&lt;/p&gt;

&lt;p&gt;Here are a few resources to get you up and running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ionic-team/react-hooks-demo-app" rel="noopener noreferrer"&gt;Demo app with example usages of all the hooks we have so far.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ionic-team/ionic-react-hooks" rel="noopener noreferrer"&gt;Ionic React Hooks Repo with docs in the readme.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for your time today, and we hope you check it out and let us know how it goes. &lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>reacthooks</category>
      <category>ionic</category>
    </item>
    <item>
      <title>Adding Ionic React to an existing React project</title>
      <dc:creator>Ely Lucas</dc:creator>
      <pubDate>Wed, 16 Oct 2019 19:42:32 +0000</pubDate>
      <link>https://dev.to/ionic/adding-ionic-react-to-an-existing-react-project-4kib</link>
      <guid>https://dev.to/ionic/adding-ionic-react-to-an-existing-react-project-4kib</guid>
      <description>&lt;p&gt;Earlier this week, we launched Ionic React (read the announcement &lt;a href="https://ionicframework.com/blog/announcing-ionic-react/"&gt;here&lt;/a&gt;). To get started with a new Ionic React project is quite easy, but what if you have an existing React project and want to integrate Ionic into it?&lt;/p&gt;

&lt;p&gt;Fortunately the process it pretty easy. In this short guide, I'll go through how to start using Ionic React in an existing project. I'll start with how to use individual components, and then touch on how to get the full Ionic app experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Individual Ionic Components
&lt;/h2&gt;

&lt;p&gt;Ionic React has around 100 components that you can begin using in your app immediately to help make it more mobile-friendly.&lt;/p&gt;

&lt;p&gt;To get started with just using components, add the &lt;code&gt;@ionic/react&lt;/code&gt; package to your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @ionic/react
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, import the core CSS for Ionic somewhere in your main app file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react/css/core.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, you can import any of the components and begin to use them right away. Here we are importing the &lt;code&gt;IonButton&lt;/code&gt; and &lt;code&gt;IonDatetme&lt;/code&gt; components and using them anywhere in our app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;IonButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IonDatetime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonDatetime&lt;/span&gt; &lt;span class="na"&gt;displayFormat&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"MM/DD/YYYY"&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Select Date"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonDatetime&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonButton&lt;/span&gt; &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"clear"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Start&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Ionic Pages
&lt;/h2&gt;

&lt;p&gt;If you want to convert part of your app and give it the whole Ionic experience, there are a few additional steps to take to get this setup.&lt;/p&gt;

&lt;p&gt;First, import some additional CSS files that help set up the overall structure of the page and some utility helpers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Basic CSS for apps built with Ionic */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react/css/normalize.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react/css/structure.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react/css/typography.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/* Optional CSS utils that can be commented out */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react/css/padding.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react/css/float-elements.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react/css/text-alignment.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react/css/text-transformation.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react/css/flex-utils.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ionic/react/css/display.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you are using another CSS framework (like Bootstrap), you might want to isolate the Ionic pages away from them. This will help to ensure there aren't any CSS conflicts between the libraries.&lt;/p&gt;

&lt;p&gt;Next, install the &lt;code&gt;@ionic/react-router&lt;/code&gt; library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @ionic/react-router
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The Ionic React Router library is a small wrapper around the popular React Router library and helps provide the functionality we need for native-like page transitions.&lt;/p&gt;

&lt;p&gt;The main Ionic page will need a couple of base components. First, use a &lt;code&gt;IonApp&lt;/code&gt; component (from &lt;code&gt;@ionic/react&lt;/code&gt;) as the root component, and then use &lt;code&gt;IonReactRouter&lt;/code&gt; (from &lt;code&gt;@ionic/react-router&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;IonApp&lt;/code&gt; sets up our main container, with the necessary styling needed for our structural components. &lt;code&gt;IonReactRouter&lt;/code&gt; is a small wrapper for React Routers &lt;code&gt;BrowserRouter&lt;/code&gt; and should be used in its place.&lt;/p&gt;

&lt;p&gt;Then, wrap all your routes in an &lt;code&gt;IonRouterOutlet&lt;/code&gt;, which is what manages our Ionic pages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonApp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonReactRouter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonRouterOutlet&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;exact&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;exact&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonRouterOutlet&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonReactRouter&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonApp&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now you can setup Ionic pages like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonPage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonHeader&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonToolbar&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonTitle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;My&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonTitle&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonToolbar&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonHeader&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonContent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonDatetime&lt;/span&gt; &lt;span class="nx"&gt;displayFormat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MM/DD/YYYY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Select Date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonDatetime&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IonButton&lt;/span&gt; &lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clear&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Start&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonButton&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonContent&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/IonPage&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that having &lt;code&gt;IonPage&lt;/code&gt; is important to have as the base component for your "Ionic" pages. &lt;code&gt;IonPage&lt;/code&gt; is the element we look for to do the page transition on.&lt;/p&gt;

&lt;p&gt;For more information on routing and navigation in Ionic React, see &lt;a href="https://ionicframework.com/docs/react/navigation"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customize the Theme
&lt;/h2&gt;

&lt;p&gt;To customize the look and feel of the components, we have some CSS variables you can override to provide a theme for your components. Set these somewhere in your main CSS file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-angular&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ac282b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-communication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#8e8d93&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-tooling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fe4c52&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fd8b2d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-design&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fed035&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-workshop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#69bb7b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-food&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3bc7c4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-documentation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#b16be3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-navigation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#6600cc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;--ion-color-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3880ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-primary-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-primary-contrast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-primary-contrast-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-primary-shade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3171e0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-primary-tint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4c8dff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;--ion-color-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0cd1e8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-secondary-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;209&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;232&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-secondary-contrast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-secondary-contrast-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-secondary-shade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0bb8cc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-secondary-tint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#24d6ea&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;--ion-color-tertiary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#7044ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-tertiary-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;112&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-tertiary-contrast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-tertiary-contrast-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-tertiary-shade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#633ce0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-tertiary-tint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#7e57ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;--ion-color-success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#10dc60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-success-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;220&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;96&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-success-contrast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-success-contrast-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-success-shade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0ec254&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-success-tint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#28e070&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;--ion-color-warning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffce00&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-warning-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;206&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-warning-contrast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-warning-contrast-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-warning-shade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e0b500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-warning-tint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffd31a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;--ion-color-danger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f04141&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-danger-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;245&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;61&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;61&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-danger-contrast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-danger-contrast-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-danger-shade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d33939&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-danger-tint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f25454&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;--ion-color-dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#222428&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-dark-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;34&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-dark-contrast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-dark-contrast-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-dark-shade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1e2023&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-dark-tint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#383a3e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;--ion-color-medium&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#989aa2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-medium-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;152&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;154&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;162&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-medium-contrast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-medium-contrast-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-medium-shade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#86888f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-medium-tint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#a2a4ab&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;--ion-color-light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f4f5f8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-light-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;244&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;244&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;244&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-light-contrast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-light-contrast-rgb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-light-shade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d7d8da&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--ion-color-light-tint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f5f6f9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For more info on theming your Ionic app, see the guide &lt;a href="https://ionicframework.com/docs/theming/themes"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Adding Ionic React to an existing React project is fairly simple and can be done in just a few minutes.&lt;/p&gt;

&lt;p&gt;The great thing about using individual components from Ionic React is that you only import the component you need. Each component is lazy loaded at runtime so that it won't bloat your bundle size. This makes Ionic React ideal for adding it to existing projects that need to look and work great on mobile devices. &lt;/p&gt;

</description>
      <category>ionic</category>
      <category>react</category>
    </item>
    <item>
      <title>Announcing the Ionic React Release Candidate!</title>
      <dc:creator>Ely Lucas</dc:creator>
      <pubDate>Wed, 14 Aug 2019 14:40:24 +0000</pubDate>
      <link>https://dev.to/ionic/announcing-the-ionic-react-release-candidate-2ok5</link>
      <guid>https://dev.to/ionic/announcing-the-ionic-react-release-candidate-2ok5</guid>
      <description>&lt;p&gt;
Today we are excited to announce that the &lt;strong&gt;Release Candidate&lt;/strong&gt; for Ionic React has launched and is now available!
&lt;/p&gt;

&lt;p&gt;We released the first &lt;a href="https://ionicframework.com/blog/announcing-the-ionic-react-beta/" rel="noopener noreferrer"&gt;Beta of Ionic React&lt;/a&gt; in February, and since then, we have received a ton of feedback and contributions from the community. Based on this feedback, we have been working to make Ionic React a great experience not only for React developers but for anyone looking to jump into web development.&lt;/p&gt;

&lt;p&gt;Ionic React RC marks the first major release of our vision to bring Ionic development to more developers on other frameworks. This was made possible by Ionic v4.0, which was completely re-written from the ground up focusing on web standards and not dependent on a particular framework. Ionic v4.0 makes it possible for us to target many frameworks while still having our core components be a single code base shared across all these frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Ionic React?
&lt;/h2&gt;

&lt;p&gt;If you can use Ionic with any framework, why are there specialized packages like &lt;code&gt;@ionic/react&lt;/code&gt;? This is a good and fair question. &lt;/p&gt;

&lt;p&gt;While you could import the core Ionic components directly into your React project, the developer experience is not where we think it should be when working with Ionic. While working with web components is possible in React, there is some boilerplate code you would have to write to properly communicate with the web components.&lt;/p&gt;

&lt;p&gt;Ionic React is a thin wrapper around our core components, exporting them as native React components and handling this boilerplate code for you. Thus using &lt;code&gt;@ionic/react&lt;/code&gt; feels natural when working in React, and offers many React paradigms that aren't there in the core components.&lt;/p&gt;

&lt;p&gt;There are also a few features that were still required to be written in the native framework, such as page lifetime management and lifecycle methods. To accomplish this, we extend the popular &lt;code&gt;react-router&lt;/code&gt; package with &lt;code&gt;@ionic/react-router&lt;/code&gt;. It is important to note that we are not creating a new router, but just extending React Router to provide a better experience when working with Ionic apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you haven't already done so, grab the latest &lt;a href="https://ionicframework.com/blog/ionic-cli-v5-brings-react-beta-support/" rel="noopener noreferrer"&gt;v5 release&lt;/a&gt; of the Ionic CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i ionic &lt;span class="nt"&gt;-g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And create an Ionic React project by specifying the type of "react":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;ionic start MyReactApp &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;react
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You will see that all of our standard starter templates are there, choose your favorite (I'll choose tabs).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ACS_85gS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2019/08/start_ionic_react_app_cli.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ACS_85gS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2019/08/start_ionic_react_app_cli.png" alt="" class="aligncenter size-full wp-image-2955"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Ionic CLI will create your project for you and install dependencies. Under the hood, the CLI uses &lt;a href="https://facebook.github.io/create-react-app/" rel="noopener noreferrer"&gt;Create React App&lt;/a&gt; (CRA) to initialize the React bits of the project.  If you are familiar with CRA then all the functionality provided by the CRA scripts are available to you in Ionic React.&lt;/p&gt;

&lt;p&gt;Next, go into the new folder (MyReactApp), and run &lt;code&gt;ionic serve&lt;/code&gt;. Your app will be compiled and launched into a new browser window:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NyU_Qnce--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2019/08/ionic-react-rc-starter.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NyU_Qnce--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.ionicframework.com/wp-content/uploads/2019/08/ionic-react-rc-starter.png" alt="" class="aligncenter size-full wp-image-2950"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a more in-depth getting started tutorial, check out our &lt;a href="https://ionicframework.com/docs/react/your-first-app" rel="noopener noreferrer"&gt;Build your First App&lt;/a&gt; guide for Ionic React in the docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whats Next?
&lt;/h2&gt;

&lt;p&gt;Now that Ionic React RC is out, we want you to test it out and send us your feedback. To report an issue or comment, head over to the &lt;a href="https://github.com/ionic-team/ionic/issues" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; and tag the issue with "package react". You can also head over to the &lt;a href="https://forum.ionicframework.com/" rel="noopener noreferrer"&gt;forums&lt;/a&gt; to ask questions or start a discussion.&lt;/p&gt;

&lt;p&gt;For the Ionic Team, our focus is set on releasing Ionic React final "soon". We will be looking closely at any issues that pop up during the RC phase and working on some final code stabilization and minor bug fixes. We don't expect any of the APIs to have any more significant changes.&lt;/p&gt;

&lt;p&gt;We also plan on creating some more content and guides in the docs to help with some best practices we've found when working with Ionic React.&lt;/p&gt;

&lt;p&gt;Until then, keep sending us your feedback and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; If you are heading out to &lt;a href="https://www.reactrally.com/" rel="noopener noreferrer"&gt;React Rally&lt;/a&gt; on August 22nd - 23rd, stop by our booth and say Hi! Several members of the core team will be out there and we would love to chat! We might even have something new to talk about then...&lt;/p&gt;




&lt;p&gt;Resources linked above in one convenient little place:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ionicframework.com/docs/react/your-first-app" rel="noopener noreferrer"&gt;Build Your First Ionic React App&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/ionic-team/ionic/tree/master/packages/react" rel="noopener noreferrer"&gt;Ionic React Project on Github&lt;/a&gt;&lt;br&gt;
&lt;a href="https://forum.ionicframework.com/" rel="noopener noreferrer"&gt;Ionic Forums&lt;/a&gt;&lt;br&gt;
&lt;a href="https://facebook.github.io/create-react-app/" rel="noopener noreferrer"&gt;Create React App&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>announcements</category>
      <category>ionic</category>
    </item>
  </channel>
</rss>
