<?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>Forem: Erik Hanchett</title>
    <description>The latest articles on Forem by Erik Hanchett (@erikch).</description>
    <link>https://forem.com/erikch</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%2F1994%2F0j84YwMs.jpeg</url>
      <title>Forem: Erik Hanchett</title>
      <link>https://forem.com/erikch</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/erikch"/>
    <language>en</language>
    <item>
      <title>I Tried Vite+ and Replaced My Entire Frontend Toolchain</title>
      <dc:creator>Erik Hanchett</dc:creator>
      <pubDate>Thu, 19 Mar 2026 17:02:19 +0000</pubDate>
      <link>https://forem.com/erikch/i-tried-vite-and-replaced-my-entire-frontend-toolchain-4cgb</link>
      <guid>https://forem.com/erikch/i-tried-vite-and-replaced-my-entire-frontend-toolchain-4cgb</guid>
      <description>&lt;p&gt;If you're a frontend developer in 2026, you've probably had to deal with a lot of different tooling. Package managers, linters, formatters, pre-commit hooks, test runners. The list goes on. And every team has opinions on which ones to use.&lt;/p&gt;

&lt;p&gt;I've worked on apps where hours if not days went into tweaking, upgrading, and swapping out tooling. And the &lt;a href="https://en.wikipedia.org/wiki/Law_of_triviality" rel="noopener noreferrer"&gt;bikeshedding&lt;/a&gt; when someone proposes something new in a meeting? Don't get me started. Earlier in my career I remember spending over an hour debating whether we should add Vitest or keep the old test runner. We ended up not changing anything.&lt;/p&gt;

&lt;p&gt;That's why I really like &lt;a href="https://viteplus.dev/" rel="noopener noreferrer"&gt;Vite+&lt;/a&gt;. Vite+ is a unified toolchain that acts as an entrypoint for web development. It comes out of the team from &lt;a href="https://voidzero.dev/" rel="noopener noreferrer"&gt;VoidZero&lt;/a&gt;, the company founded by Evan You, the creator of Vite and Vue. You can think of it like a ShamWow for your toolchain. One tool that replaces your linter, formatter, test runner, bundler, and package manager all at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I also made a full video walkthrough where I set up a Vue project from scratch, run into the Nuxt issues in real time, and show the formatting and testing in action. If you'd rather watch than read, check it out below!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/2C1HkBrz5Wc"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What's inside Vite+&lt;/li&gt;
&lt;li&gt;A few caveats&lt;/li&gt;
&lt;li&gt;Getting started&lt;/li&gt;
&lt;li&gt;Creating your first app&lt;/li&gt;
&lt;li&gt;
Important commands

&lt;ul&gt;
&lt;li&gt;Checking and linting&lt;/li&gt;
&lt;li&gt;Formatting&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Caching with vp run&lt;/li&gt;
&lt;li&gt;Managing Node.js with vp env&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's inside Vite+
&lt;/h2&gt;

&lt;p&gt;Instead of having to worry about building, installing, adding every tool you need when building an app, and dealing with configurations in 10 different files, you can now rely on one Vite powered toolchain. In essence, Vite+ combines all of these tools into one. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vite.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; as your build tool, now even faster with &lt;a href="https://vite.dev/blog/announcing-vite8" rel="noopener noreferrer"&gt;Vite 8&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vitest.dev/" rel="noopener noreferrer"&gt;Vitest&lt;/a&gt; for testing&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://oxc.rs/docs/guide/usage/linter.html" rel="noopener noreferrer"&gt;Oxlint&lt;/a&gt; for linting, replacing ESLint&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://oxc.rs/docs/guide/usage/formatter.html" rel="noopener noreferrer"&gt;Oxfmt&lt;/a&gt; for formatting, replacing Prettier and Biome&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://rolldown.rs/" rel="noopener noreferrer"&gt;Rolldown&lt;/a&gt; for bundling, replacing ESBuild&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://tsdown.dev/" rel="noopener noreferrer"&gt;tsdown&lt;/a&gt; for library bundling&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; runtime and package manager built in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since many of these tools are written in Rust, the performance gains are real. According to &lt;a href="https://voidzero.dev/posts/announcing-vite-plus-alpha" rel="noopener noreferrer"&gt;VoidZero's announcement&lt;/a&gt;, Oxlint runs 50 to 100x faster than ESLint, Oxfmt formats up to 30x faster than Prettier, and production builds with Vite 8 and Rolldown are 1.6x to 7.7x faster than Vite 7. I had to test this out in my own projects, and using the &lt;code&gt;vp check&lt;/code&gt; command was finishing in under a second on fairly large code bases.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few caveats
&lt;/h2&gt;

&lt;p&gt;While having everything in one tool helps, it does have some DX papercuts at this point. For example, while they do support most package managers (pnpm, npm, yarn), they are missing &lt;a href="https://bun.sh/" rel="noopener noreferrer"&gt;bun&lt;/a&gt;. There is also some more work to be done with the vite.config.ts on Nuxt and TanStack Start since the tooling for each aren't quite there yet. Nevertheless it's early days in Vite+, and it's still in alpha, so I'm sure the updates are happening soon.&lt;/p&gt;

&lt;p&gt;I'd imagine if you are not sold on the Vite ecosystem, or using toolchains in general, this probably isn't the tool for you. I, like many other developers, have opinions, and often when I create apps I have very bespoke patterns I use. In my case, I use &lt;a href="https://github.com/vuejs/create-vue" rel="noopener noreferrer"&gt;&lt;code&gt;npm create vue@latest&lt;/code&gt;&lt;/a&gt; for every Vue app, and &lt;code&gt;npm create nuxt@latest&lt;/code&gt; for Nuxt apps. The Vue scaffolder does let you opt into ESLint, Prettier, and Vitest during setup, which is nice. But each one gets its own config file, you still have to make sure they play well together, and things like pre-commit hooks are on you to wire up with husky or lint-staged. It adds up. I also have a very specific set of skills and markdown files I use with &lt;a href="https://kiro.dev" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;, to help me code.&lt;/p&gt;

&lt;p&gt;With Vite+, all of that comes out of the box. One &lt;code&gt;vp create vue&lt;/code&gt; and I have linting, formatting, testing, and pre-commit hooks ready to go in a single &lt;code&gt;vite.config.ts&lt;/code&gt;. That's what sold me on it.&lt;/p&gt;

&lt;p&gt;Let's take a look at how to set it up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;As described in the &lt;a href="https://viteplus.dev/guide/" rel="noopener noreferrer"&gt;Vite+&lt;/a&gt; setup guide you can install it on your macOS/Linux with a simple curl command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://vite.plus | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should be it!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;During the install you'll be asked a few questions around how Vite+ should manage your global Node.js runtime and package manager. You can opt-out of this, and use &lt;code&gt;nvm&lt;/code&gt; or whatever else you'd like. However, I actually think this is really nice feature. With Vite+ you can use the &lt;code&gt;vp env&lt;/code&gt; command to switch between environments really easily. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From here you can either start creating apps, or migrate existing Vite apps over. I've only really tested the creating apps options, so we'll look at that. I have seen a few people using the &lt;code&gt;vp migrate&lt;/code&gt; command to do migration. It seems to work OK, but still requires manual intervention. One cool thing from the &lt;a href="https://voidzero.dev/posts/announcing-vite-plus-alpha" rel="noopener noreferrer"&gt;Vite+ announcement&lt;/a&gt; is that they provide a migration prompt you can paste directly into your AI coding agent. So if you're using &lt;a href="https://kiro.dev" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; or &lt;a href="https://kiro.dev/cli/" rel="noopener noreferrer"&gt;kiro-cli&lt;/a&gt;, you could have it handle the migration for you and just review the changes after. Here's the prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Migration prompt:&lt;/strong&gt; Migrate this project to Vite+. Run &lt;code&gt;vp help&lt;/code&gt; to understand Vite+'s capabilities. Migrations can be run using &lt;code&gt;vp migrate&lt;/code&gt;. Run &lt;code&gt;vp help migrate&lt;/code&gt; for options. After the migration, verify the changes and make sure that type checking, linting, formatting, and tests pass. High-five your human when you are done.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Creating your first app
&lt;/h2&gt;

&lt;p&gt;After installation you can then create a new app using the &lt;code&gt;vp create&lt;/code&gt; command. This will then ask a few quick questions on what kind of application you'd like and it will then scaffold it all for you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmi7didlbl8bw8civzgjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmi7didlbl8bw8civzgjr.png" alt="Image of vp create"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As of this blog post it defaults to a non JS Framework solution. While this is fine for a demo, I don't really get it, because I don't think many people today create websites without some sort of framework or library. Instead you can use this command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vp create &amp;lt;template&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Right now it supports, &lt;code&gt;vite&lt;/code&gt;, &lt;code&gt;@tanstack/start&lt;/code&gt;, &lt;code&gt;svelte&lt;/code&gt;, &lt;code&gt;next-app&lt;/code&gt;, &lt;code&gt;nuxt&lt;/code&gt;, &lt;code&gt;react-router&lt;/code&gt; and &lt;code&gt;vue&lt;/code&gt;. As I mentioned earlier with &lt;code&gt;Nuxt&lt;/code&gt; they have not combined the &lt;code&gt;nuxt.config.ts&lt;/code&gt; and the &lt;code&gt;vite.config.ts&lt;/code&gt; together, so while you can use it, it's not quite ready yet. I also had issues with the &lt;code&gt;next-app&lt;/code&gt;. I wasn't sure if it was trying to use a Vite-based Next app or the standard one. Either way it wasn't quite working.&lt;/p&gt;

&lt;p&gt;After adding a template name it will then setup your app. It should look familiar if you're using something like &lt;code&gt;npm create vite&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;After install you'll then have a unified vite.config.ts file that you can then enter all your configurations for linting, formatting and testing. This worked surprisingly well and I was happy not having to worry about where to add each file.&lt;/p&gt;

&lt;p&gt;Since Vite+ uses the &lt;a href="https://oxc.rs/" rel="noopener noreferrer"&gt;Oxc&lt;/a&gt; set of tools, I added in the official Oxc extension for formatting and linting. This made it even easier so that my IDE was aware of these tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Important commands
&lt;/h2&gt;

&lt;p&gt;Beyond &lt;code&gt;vp create&lt;/code&gt; you have a number of interesting commands you can run with &lt;code&gt;vp&lt;/code&gt;. Here are a few I've found useful. Although, if you'd like a full list please check out the official &lt;a href="https://viteplus.dev/guide/" rel="noopener noreferrer"&gt;guide&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Checking and linting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vp check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The vp check is the default command for fast static checks in Vite+. It combines both the formatting with Oxfmt and the linting with Oxlint, and the TypeScript type checks via &lt;a href="https://github.com/oxc-project/tsgolint" rel="noopener noreferrer"&gt;tsgolint&lt;/a&gt;. Combined this is an extremely fast static checker.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Under the hood, tsgolint is powered by &lt;a href="https://github.com/microsoft/typescript-go" rel="noopener noreferrer"&gt;tsgo&lt;/a&gt;, the official Go port of the TypeScript compiler from Microsoft. This means your type checking is running at native speed, not through Node.js. It's a big reason why &lt;code&gt;vp check&lt;/code&gt; feels so fast.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you like you can have it try to fix the issues.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vp check &lt;span class="nt"&gt;--fix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my tests, this still takes a lot of manual intervention as expected. What's nice is that a competent AI coding tool, like &lt;a href="https://kiro.dev" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;, can knock these type errors out in no time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Vite+ also has built in pre-commit hook support. When you scaffold a project with &lt;code&gt;vp create&lt;/code&gt;, it can set up hooks for you automatically. You can also run &lt;code&gt;vp prepare&lt;/code&gt; to install them later, and configure what runs on staged files right in your &lt;code&gt;vite.config.ts&lt;/code&gt;. No more husky or lint-staged setup.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Formatting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vp &lt;span class="nb"&gt;fmt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run the &lt;a href="https://oxc.rs/docs/guide/usage/formatter.html" rel="noopener noreferrer"&gt;Oxfmt&lt;/a&gt; formatter. It has full &lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; compatibility and is designed as a drop-in replacement for Prettier.&lt;/p&gt;

&lt;p&gt;Personally, I love prettier, but it does have its quirks. What's really nice is you can just go into the &lt;code&gt;vite.config.ts&lt;/code&gt; file and add any rules you need.&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;defineConfig&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;vite-plus&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;singleQuote&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I first setup my app using &lt;code&gt;vp create vue&lt;/code&gt; it asked me if I'd like to update the &lt;code&gt;.vscode/settings.json&lt;/code&gt;. I'd highly recommend this so if you're using the Oxfmt extension it can find your formatting settings. Otherwise you can manually add it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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="nl"&gt;"oxc.fmt.configPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./vite.config.ts"&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;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;I love testing, so running tests with &lt;a href="https://vitest.dev/" rel="noopener noreferrer"&gt;Vitest&lt;/a&gt; is great.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vp &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will kickoff the vitest test runner in your project. There is also a &lt;code&gt;watch&lt;/code&gt; and a &lt;code&gt;run --coverage&lt;/code&gt; command. And like all the other commands you can edit the configuration in the &lt;code&gt;vite.config.ts&lt;/code&gt; file.&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;defineConfig&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;vite-plus&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/**/*.test.ts&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;One quick annoyance with running the &lt;code&gt;vp&lt;/code&gt; commands is that they do override your package.json scripts. So to run anything in your scripts you must run &lt;code&gt;vp run &amp;lt;command&amp;gt;&lt;/code&gt;. Otherwise it will run the built in commands. I was watching a live stream with Theo Browne from &lt;a href="https://t3.gg/" rel="noopener noreferrer"&gt;T3&lt;/a&gt; and he hated this. I'm on the fence, but maybe they'll change it in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Caching with vp run
&lt;/h3&gt;

&lt;p&gt;Another nice feature is caching.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vp run &lt;span class="nt"&gt;--cache&lt;/span&gt; build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will cache the build and if nothing changes the output is replayed on the next run. I have not used this command personally but anything to speed up builds I'm all for. &lt;/p&gt;

&lt;h3&gt;
  
  
  Managing Node.js with vp env
&lt;/h3&gt;

&lt;p&gt;Another really neat feature is the &lt;code&gt;vp env&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vp &lt;span class="nb"&gt;env&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;vp env manages Node.js versions globally and per project. By default, Vite+ runs in managed mode, meaning it controls which Node.js version is used. It stores the runtime and related files in ~/.vite-plus (you can override this with VITE_PLUS_HOME).&lt;/p&gt;

&lt;p&gt;If you don't want Vite+ managing your Node.js, you can switch to system-first mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vp &lt;span class="nb"&gt;env &lt;/span&gt;off
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells the shims to prefer your system Node.js and only fall back to the Vite+ managed runtime when needed. If you ever want to switch back, just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vp &lt;span class="nb"&gt;env &lt;/span&gt;on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also pin a certain Node.js version in the current directory with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vp &lt;span class="nb"&gt;env &lt;/span&gt;pin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or install an entirely new version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vp &lt;span class="nb"&gt;env install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I really liked all this flexibility with the package manager. I personally use &lt;code&gt;nvm&lt;/code&gt; but I like the idea of this being handled by Vite+. One less thing to worry about.&lt;/p&gt;

&lt;p&gt;While I haven't tried it, I'm curious to see how well this works in a CI environment. It looks like you can easily set this up in your GitHub actions and run all the checks you need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;voidzero-dev/setup-vp@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;22'&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vp install&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vp check&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vp test&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vp build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I could definitely see Vite+ saving me time when I create a new application. Having one configuration file to put everything in, and not having to think about which linter, formatter, or test runner to install, is a nice change. The &lt;code&gt;vp&lt;/code&gt; commands are straightforward and the whole thing just works.&lt;/p&gt;

&lt;p&gt;With that said, this project is still in alpha, and we'll see how much it catches on. With so much of the frontend ecosystem already using Vite, it makes sense for a tool like Vite+ to exist. Let me know if you would use something like this. I think it's very interesting. Thanks!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>vite</category>
      <category>frontend</category>
      <category>javascript</category>
    </item>
    <item>
      <title>5 Things To Avoid When Working With AI Coding Tools</title>
      <dc:creator>Erik Hanchett</dc:creator>
      <pubDate>Thu, 12 Mar 2026 21:10:42 +0000</pubDate>
      <link>https://forem.com/aws/5-things-to-avoid-when-working-with-ai-tools-5cld</link>
      <guid>https://forem.com/aws/5-things-to-avoid-when-working-with-ai-tools-5cld</guid>
      <description>&lt;p&gt;As a software developer in 2026 you can't escape AI. It's everywhere, and almost every company is using some sort of AI coding tool. And as a long time full-stack developer whose roots are in the front-end, I wasn't always convinced. &lt;/p&gt;

&lt;p&gt;Over the past two years I've seen both sides of AI. The terrible designs, but also the surprisingly decent ones. Demo apps that used to take me hours, done in minutes. But I've also found myself knee-deep in AI slop, wondering if I actually saved any time at all. &lt;/p&gt;

&lt;p&gt;So are these coding tools making me faster or not? I had to look into it more.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to watch a video on the subject then read about it? Check this out!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/LHWt5ybRFk0"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Last year, Model Evaluation and Threat Research, or METR, ran a &lt;a href="https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/" rel="noopener noreferrer"&gt;randomized controlled trial&lt;/a&gt; and found that when experienced open source developers used AI tools, it took them 19% longer to complete tasks than without. AI was actually a detriment to their productivity.&lt;/p&gt;

&lt;p&gt;On the other hand, GitHub conducted their own &lt;a href="https://github.blog/news-insights/research/research-quantifying-github-copilots-impact-on-code-quality/" rel="noopener noreferrer"&gt;controlled trial&lt;/a&gt; around the same time and found that developers coded up to 55% faster with AI assistance.&lt;/p&gt;

&lt;p&gt;So which is it?&lt;/p&gt;

&lt;p&gt;After trying these tools out myself for the last two years, I can confidently say I think both are true. And the difference comes down to the approach used. AI tools, just like any other tool, can be misused. If the only tool you have is a hammer, every problem starts to look like a nail. When I first started using AI, I used it for everything. New features, bug fixes, refactors, brainstorming, all of it. And at first it felt amazing. But I kept running into the same problems over and over. The output looked good on the surface, but I'd spend more time fixing what it gave me than if I'd just written it myself.&lt;/p&gt;

&lt;p&gt;Here are 5 ways AI can hurt you instead of help you, especially on the frontend.&lt;/p&gt;

&lt;h3&gt;
  
  
  No Real Feature Definition
&lt;/h3&gt;

&lt;p&gt;I like jumping into a coding agent right away. I start vibe coding as soon as my fingers reach the keyboard, but this actually isn't a great way to start. The AI will absolutely give me what I ask for, and it might even work, but the problem lies in the details.&lt;/p&gt;

&lt;p&gt;The design generated is often not great (I'm looking at you GPT) and the features don't always work. Validation, error handling, responsiveness, and accessibility will often be partially implemented or skipped entirely. That's when things fall apart.&lt;/p&gt;

&lt;p&gt;The real issue is that you need to define what success looks like. If you don't, the AI just guesses, and though it gets some of it right, it gets a lot wrong. Ambiguity isn't your friend.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The real issue is that you need to define what success looks like.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Don't get me wrong, you don't need a 15 page requirements document with detailed designs for every use case. However, maybe a few bullet points and some basic acceptance criteria will help. Simply defining what the feature should do and not do is the minimum you should ask. Adding this all to a markdown file before you get started will dramatically improve what the AI generates for you.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Too Much Bad Context
&lt;/h3&gt;

&lt;p&gt;So that means I should put everything into the context window? Use as many AGENTS.md and markdown files as I can before I start, right? Well, yes we need more context but there is more to it than that.&lt;/p&gt;

&lt;p&gt;The second problem is putting too much into your context window. Whenever you work with a coding tool, I prefer &lt;a href="https://kiro.dev" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; so I'll use that as an example, it has a defined amount of space it can handle. This is often influenced by the model you select. Some models have larger context windows, others have smaller.&lt;/p&gt;

&lt;p&gt;Either way, stuffing everything you can find into lots of markdown files causes the opposite problem from #1. Now the model has to comb through lots of useless information to find what it needs. We've recently seen research that shows that more is not always better.&lt;/p&gt;

&lt;p&gt;Recent research across over 60,000 repos found that context files are often too long, too vague, and are actively making agents worse. In one study, accuracy dropped from 87% to 54% just from context overload.&lt;/p&gt;

&lt;p&gt;Context overload doesn't just drop accuracy, it can also increase token cost. When every request sends more information than is needed, you uselessly waste tokens, which in turn hits your pocketbook.&lt;/p&gt;

&lt;p&gt;At the end of the day, it's more about quality than quantity when you're dealing with context windows. Think about it like giving directions. If someone asks you how to get to the grocery store and you hand them a 200-page atlas, that's technically more information. But it's not more helpful.&lt;/p&gt;

&lt;p&gt;When creating an AGENTS.md file (or steering file if you're using Kiro), only include information that is needed for your project. Constrain it to your coding practices, tabs vs spaces, design system, API contracts and rendering. Make sure to remove unneeded fluff, and keep it up to date. When I use Kiro, I create an agent that automatically updates my markdown files when I make changes to my components. That way I always have the latest updates in my context files.&lt;/p&gt;

&lt;p&gt;There's probably a Goldilocks zone of context. Not too little, not too much. Just the stuff that actually matters for the task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Too Much in One Shot
&lt;/h3&gt;

&lt;p&gt;While having too much or too little in your context is important, you still need to know the limitations of what you're asking your coding agent to do. One common problem I see is that developers will try to one-shot a whole application. I'll see prompts to build a frontend, backend, tests, create all the things at once. It feels like you're making a lot of progress, and it outputs something in just a few minutes.&lt;/p&gt;

&lt;p&gt;This could actually work for a quick prototype or demo. However, in production this is not what you want. You'll end up with a lot of AI slop that often takes a lot of rework.&lt;/p&gt;

&lt;p&gt;I was reading the other day that a whole industry has popped up to help small teams fix their vibe-coded messes. This should really tell you something about the state of the industry. &lt;/p&gt;

&lt;p&gt;The reality is that creating everything at once causes the AI to lose architectural consistency. It may solve the same problem different ways in different files. It creates patterns that are contradictory or nonsensical.&lt;/p&gt;

&lt;p&gt;To fix this issue, you need to scope down the tasks. Unless you're running some autonomous agent loop that's going to churn through a large detailed requirements document and tasks for hours, you'll need to break things down. Build this component, refactor these tests. Here are the edge cases for this part of the app. The key, as we'll discuss in the next section, is to check the output. It's a lot easier to catch a problem in 50 lines than 2,000.&lt;/p&gt;

&lt;p&gt;This problem has actually been mitigated in a lot of ways with spec-driven development. I use Kiro all the time to break down complex features into requirements, design and tasks. That way you can check the plan before the AI writes any code at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Too Much Trust
&lt;/h3&gt;

&lt;p&gt;As hinted in the last paragraph, we should not be trusting the output of AI at face value. And of course, it does look convincing at first. When I started using Claude 4.5 Opus, I really thought coding was a solved problem. The code passed my linter, the types looked ok, and it ran well. &lt;/p&gt;

&lt;p&gt;But as I started looking at the code in more detail, I saw some issues and edge cases I didn't like. Maybe one day AI will write 100% accurate code, but we still aren't there yet.&lt;/p&gt;

&lt;p&gt;There is no question AI makes writing code faster, but you need to spend more time checking the output. In other words, AI compresses generation time, but it often expands verification time. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI compresses generation time, but it often expands verification time. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'll be the first to admit, I love writing code, debugging it, finding clever ways to abstract the logic, and seeing it run for the first time. I'm not as big of a fan of code reviews. Something about reviewing other people's code and giving feedback isn't as fun.&lt;/p&gt;

&lt;p&gt;However, when AI is doing the writing, reviewing becomes the most important part of your job. For example, on the frontend I've seen things like weak accessibility, brittle logic, weird abstractions, duplicated behavior, and terrible design. Let me emphasize again that AI is not great at design. It's passable if you love purple and you don't mind generic Tailwind-looking apps. You really need to iterate over it several times to get a good outcome.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI is not great at design!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To help mitigate these problems you can set up tests, and those help, but sometimes the AI games those too. Really though, there is no better way than having a real life breathing human verify everything along the way. You should read the code, understand it, gauge the design, and test the assumptions.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Speed Over Maintainability
&lt;/h3&gt;

&lt;p&gt;As a developer, I love writing code fast. I remember being a young software developer and learning as many hotkeys and keystrokes as I could. I learned Vim so I would never have to touch the mouse, because I knew that every second I touched it, it would slow me down.&lt;/p&gt;

&lt;p&gt;With AI writing your code, speed stops being the hard part. I'll never type faster than a large language model can generate. But that's the trap, just because it's fast doesn't mean it's good. &lt;br&gt;
When code gets cheap to generate, bad abstractions get cheaper too.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When code gets cheap to generate, bad abstractions get cheaper too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI will over-generate wrappers, components, abstractions and APIs. These AI tools are like eager interns, they want to show off what they can do. But if you don't monitor them closely they'll go off the rails. &lt;/p&gt;

&lt;p&gt;And my cognitive load can only handle so much. I remember one of the first codebases I worked on had so many levels of abstraction, it took me 15 minutes of ctrl-clicking into every class and interface to figure out what it was exactly doing. (Yes this was in Java). If you let AI go off, it will do the same thing. &lt;/p&gt;

&lt;p&gt;You end up owning code you didn't fully think through or really understand. Your job is to understand what the AI is creating, because six months from now, someone has to maintain it. And that someone is probably you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modern Tooling
&lt;/h3&gt;

&lt;p&gt;So where are we at today? Well, we know that models are getting better all the time, and maybe in a few years most of these problems will be solved.&lt;/p&gt;

&lt;p&gt;But we are not there yet.&lt;/p&gt;

&lt;p&gt;Tools like Kiro have spec-driven development, and other tools have plan mode, checkpoints, and better workflows. But these tools are only as good as the person driving them and there still is no silver bullet. You still need your judgement, your context, and your code review skills to be successful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;If we go back to our two studies at the start, both things can be true at once. Some developers will see productivity gains with AI tools, many others won't. It really comes down to how you use them in your day-to-day life.&lt;/p&gt;

&lt;p&gt;You can't vibe code every app, and every app shouldn't be vibe coded. You need the right amount of context, not too little, not too much. You need to scope your asks. You need to become a better code reviewer. And you need to think about whether what the AI created is something you can actually maintain six months from now.&lt;/p&gt;

&lt;p&gt;Let me know in the comments if you agree or disagree. Until next time.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>coding</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The hosting setup nobody talks about anymore</title>
      <dc:creator>Erik Hanchett</dc:creator>
      <pubDate>Fri, 20 Feb 2026 00:15:40 +0000</pubDate>
      <link>https://forem.com/aws/the-hosting-setup-nobody-talks-about-anymore-2528</link>
      <guid>https://forem.com/aws/the-hosting-setup-nobody-talks-about-anymore-2528</guid>
      <description>&lt;p&gt;Ever had this problem? &lt;/p&gt;

&lt;p&gt;&lt;em&gt;You're building something real, real-time features, background workers, cron jobs, maybe a database or two. You've outgrown the managed platforms, or you're tired of stitching together five different SaaS subscriptions to get what a single server could give you. You want to understand your infrastructure, not just deploy to it. Let me introduce you to the world of Virtual Private Servers (VPS).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A VPS gives you full control over a private server that you yourself manage. Root access, any library or service you want, no execution limits or timeouts. If something breaks, you can SSH in and fix it, no support tickets, no waiting.&lt;/p&gt;

&lt;p&gt;That flexibility comes with a tradeoff: you're responsible for the setup, the security, and the maintenance. Managed platforms abstract that away, which is exactly why they're great for simpler use cases. However when your app needs more than what a platform gives you out of the box, or when you just want to learn how the pieces fit together, running your own server is worth the investment.&lt;/p&gt;

&lt;p&gt;The purpose of this tutorial is to walk you through that setup end to end. We'll launch a VPS, configure a web server with a CDN, connect it to a domain, and wire up a deployment pipeline. By the end you'll understand every layer of your stack. Let's jump in!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Heads up&lt;/strong&gt; — this tutorial gets you to a working deployment. If you plan to serve real user traffic, check the Production Hardening section before going live.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Our Setup&lt;/li&gt;
&lt;li&gt;
Step-by-step

&lt;ul&gt;
&lt;li&gt;Launching your EC2 instance&lt;/li&gt;
&lt;li&gt;Installing nginx and Docker&lt;/li&gt;
&lt;li&gt;Configuring AWS Systems Manager&lt;/li&gt;
&lt;li&gt;Setting up the deploy directory and ECR&lt;/li&gt;
&lt;li&gt;Connecting GitHub with OIDC&lt;/li&gt;
&lt;li&gt;Adding the GitHub Action and Dockerfile&lt;/li&gt;
&lt;li&gt;Testing the deployment&lt;/li&gt;
&lt;li&gt;Setting up CloudFront, SSL, and nginx&lt;/li&gt;
&lt;li&gt;Connecting your domain with Route 53&lt;/li&gt;
&lt;li&gt;Adding cache behaviors&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Production hardening&lt;/li&gt;

&lt;li&gt;Cleanup&lt;/li&gt;

&lt;li&gt;Wrapping up&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Want a video instead? &lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/VKk-l97CNU8"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Setup
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Full disclosure, I'm a &lt;a href="https://www.linkedin.com/in/erikhanchett/" rel="noopener noreferrer"&gt;Developer Advocate for AWS&lt;/a&gt;, so I'll be using AWS services in the tutorial as it's what I'm most familiar with. I've been hosting web apps on VPS for years. Feel free to use whatever VPS provider you'd like though.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this post we are using an &lt;a href="https://aws.amazon.com/ec2/" rel="noopener noreferrer"&gt;Amazon EC2&lt;/a&gt; T3 Micro instance running Ubuntu with an nginx web server. We'll use &lt;a href="https://aws.amazon.com/systems-manager/" rel="noopener noreferrer"&gt;AWS Systems Manager&lt;/a&gt; to help set up a CI/CD pipeline using GitHub Actions. We'll then configure &lt;a href="https://aws.amazon.com/certificate-manager/" rel="noopener noreferrer"&gt;AWS Certificate Manager&lt;/a&gt; with &lt;a href="https://aws.amazon.com/cloudfront/" rel="noopener noreferrer"&gt;Amazon CloudFront&lt;/a&gt; and have it connected to our domain with &lt;a href="https://aws.amazon.com/route53/" rel="noopener noreferrer"&gt;Amazon Route 53&lt;/a&gt;! We'll be using a Vue Nuxt 4 application as our web app. &lt;/p&gt;

&lt;p&gt;Here is a high level diagram of our final output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgth23uog5ca1rrhov3yr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgth23uog5ca1rrhov3yr.png" alt="Architecture of application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Don't worry, you won't need to be an EC2 or AWS expert to follow this post. I'll assume you have some basic knowledge of software development, but that's it. To get started make sure you sign up for a &lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;free AWS account&lt;/a&gt;. I'll also assume you have some sort of application you want to deploy and it's already on &lt;a href="https://github.com" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. In this example we'll be using Nuxt with SSR, but you can use whatever you'd like. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-step
&lt;/h2&gt;

&lt;p&gt;Typically in enterprise applications you might see infrastructure as code (IAC) building and deploying applications on AWS. However, for this tutorial we'll be going directly to the AWS console. I find this the simplest way to get started.&lt;/p&gt;

&lt;p&gt;If you're following along with this tutorial, or skimming it through to get ideas, make sure to download a good agentic IDE like &lt;a href="https://kiro.dev/cli/" rel="noopener noreferrer"&gt;Kiro CLI&lt;/a&gt;. Kiro CLI can be installed remotely on your server and help you troubleshoot any production issue you have while setting up your service. I used it extensively while researching this blog post, and you should too! &lt;/p&gt;

&lt;h3&gt;
  
  
  Launching your EC2 instance
&lt;/h3&gt;

&lt;p&gt;To host our application we'll start by creating a new EC2 instance. Log into the &lt;a href="https://console.aws.amazon.com/" rel="noopener noreferrer"&gt;AWS Console&lt;/a&gt; to begin. Make sure you're on &lt;code&gt;N. Virginia us-east-1&lt;/code&gt; in the top right-hand corner.&lt;/p&gt;

&lt;p&gt;Search for &lt;code&gt;EC2&lt;/code&gt; in your AWS Console. It will bring you to a page where you can click &lt;code&gt;Launch instance&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71861xer41aujhlw01ph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71861xer41aujhlw01ph.png" alt="AWS EC2 console showing the Launch instance button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll then be brought to a screen with a lot of options. Don't worry, just fill out the name of your server. In this case I chose &lt;code&gt;My-Web-Server&lt;/code&gt; and pick an OS image. I really like Ubuntu for beginners so I chose that. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjcblmn84upi8r1o0aear.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjcblmn84upi8r1o0aear.png" alt="EC2 launch wizard showing server name and Ubuntu OS image selection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next you'll need to select an instance type. We want to keep this server cheap (and free-tier eligible), so let's go with the t3.micro instance that has 2 vCPUs and 1 GiB of memory. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsootdq6gv3dirqanpr4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsootdq6gv3dirqanpr4.png" alt="EC2 instance type selection showing t3.micro with 2 vCPUs and 1 GiB memory"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What about traffic?&lt;/strong&gt;&lt;br&gt;
I know what you're thinking, how much traffic can a t3.micro instance handle? While I can't say for sure, we'll be using a Content Delivery Network (CDN) via Amazon CloudFront and caching as much as possible to help absorb traffic spikes. And if you ever outgrow a single instance, AWS can handle it. An Auto Scaling Group (ASG) can spin up additional EC2 instances once a certain traffic threshold is met, an Application Load Balancer (ALB) can distribute incoming traffic across them, and a Web Application Firewall (WAF) can block malicious or "noisy" traffic. We won't be covering those in this post, but I'd recommend reading up on it &lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/tutorial-ec2-auto-scaling-load-balancer.html" rel="noopener noreferrer"&gt;here&lt;/a&gt; if you're interested.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Click the &lt;code&gt;Create new key pair&lt;/code&gt; button. This will send you to a popup to add a new key. Add a new key pair name, leave the rest as default and click &lt;code&gt;Create key pair&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft448qnpux6eom810gzxh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft448qnpux6eom810gzxh.png" alt="Create key pair dialog with name field and default settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will then ask you to save the key. This is very important. You'll need this key to SSH into your instance later. Save it somewhere safe.&lt;/p&gt;

&lt;p&gt;In the network settings you may see a banner that you don't have a default VPC. Click &lt;code&gt;create a new VPC&lt;/code&gt;. On the next page leave everything as default and click &lt;code&gt;Create default VPC&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2459bbzzggoe7f79shdz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2459bbzzggoe7f79shdz.png" alt="Banner prompting to create a default VPC"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should now be able to select your VPC, if it's not already selected. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1164hc59nscvkmrdm3a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1164hc59nscvkmrdm3a.png" alt="Network settings with VPC selected"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Firewall (security groups) make sure to change the &lt;code&gt;Allow SSH traffic from&lt;/code&gt; to only allow traffic from your IP. It should be listed in the dropdown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F701xhvftf78oitxycrtz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F701xhvftf78oitxycrtz.png" alt="Firewall settings restricting SSH traffic to your IP address"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the bottom click on &lt;code&gt;Launch instance&lt;/code&gt; and you'll see a nice green &lt;code&gt;Success&lt;/code&gt; banner. You can click on your instance &lt;code&gt;i-***&lt;/code&gt; to look at it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa8cfwdara33yshwpy7a6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa8cfwdara33yshwpy7a6.png" alt="Success banner after launching the EC2 instance"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next window look for the public IP address. Save it! &lt;/p&gt;

&lt;p&gt;Now jump into your favorite terminal. Make sure you're in the same directory as the &lt;code&gt;*.pem&lt;/code&gt; file you created earlier. You'll need to set some permissions on it. You'll then be able to SSH into your server. Make sure to replace &lt;code&gt;ip-address&lt;/code&gt; with the public IP address you saved from the EC2 instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;400 your-pem-file.pem
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; your-pem-file.pem ubuntu@ip-address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉 Congrats! You've logged in for the first time!&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing nginx and Docker
&lt;/h3&gt;

&lt;p&gt;Now that we have our EC2 instance up and running, let's do something with it!&lt;/p&gt;

&lt;p&gt;First update and then upgrade Ubuntu to the latest. It may ask you to restart some services. You may consider some best &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-best-practices.html" rel="noopener noreferrer"&gt;practices&lt;/a&gt; when working with EC2 as well.&lt;/p&gt;

&lt;p&gt;Let's install nginx. This will be our web server that we'll use as a reverse proxy. It listens on port 80 for incoming traffic and forwards it to our Nuxt app running on port 3000.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After nginx is installed we'll need to do some configuration. Let's add a new site under the &lt;code&gt;sites-available&lt;/code&gt; directory. We'll call it &lt;code&gt;nuxt&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/nuxt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace it with the code below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;# (we’ll enable this after CloudFront is created)&lt;/span&gt;
  &lt;span class="c1"&gt;# if ($http_x_origin_verify != "REPLACE_WITH_SECRET") { return 403; }&lt;/span&gt;

  &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;"upgrade"&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;p&gt;This is a basic setup for nginx. Check out the &lt;a href="https://nginx.org/en/docs/beginners_guide.html" rel="noopener noreferrer"&gt;beginners guide&lt;/a&gt; for more information on what each option does. The &lt;code&gt;Upgrade&lt;/code&gt; and &lt;code&gt;Connection&lt;/code&gt; headers are there so WebSocket connections work correctly — handy if your app uses real-time features. &lt;/p&gt;

&lt;p&gt;You may have noticed a comment at the top! Don't worry, we'll come back to this later when we set up CloudFront.&lt;/p&gt;

&lt;p&gt;To enable the site we have to create a symbolic link from &lt;code&gt;sites-available&lt;/code&gt; to &lt;code&gt;sites-enabled&lt;/code&gt;. We'll also do a little cleanup and remove the default nginx site and restart it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; /etc/nginx/sites-available/nuxt /etc/nginx/sites-enabled/nuxt
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /etc/nginx/sites-enabled/default
&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all goes well we'll get a successful message! If not, double-check the &lt;code&gt;nuxt&lt;/code&gt; configuration inside nginx. It's easy to copy something wrong.&lt;/p&gt;

&lt;p&gt;At this point we have a few options to handle our Nuxt site. We could just copy the dist folder over, install node, and use something like &lt;a href="https://pm2.keymetrics.io/" rel="noopener noreferrer"&gt;pm2&lt;/a&gt; to manage the node process. While this works, I find it a little brittle. &lt;/p&gt;

&lt;p&gt;Running everything on our EC2 instance requires managing dependencies directly on the system, making it harder to ensure consistent environments between development and production. Docker containers provide a better option. They isolate our dependencies, we can roll back easier, and we'll have more predictable deployments since everything your app needs is packaged together.&lt;/p&gt;

&lt;p&gt;To install Docker we'll follow the official &lt;a href="https://docs.docker.com/engine/install/ubuntu/" rel="noopener noreferrer"&gt;Docker docs&lt;/a&gt; that recommend using the &lt;code&gt;apt&lt;/code&gt; repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add Docker's official GPG key:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;ca-certificates curl
&lt;span class="nb"&gt;sudo install&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 0755 &lt;span class="nt"&gt;-d&lt;/span&gt; /etc/apt/keyrings
&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg &lt;span class="nt"&gt;-o&lt;/span&gt; /etc/apt/keyrings/docker.asc
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;a+r /etc/apt/keyrings/docker.asc

&lt;span class="c"&gt;# Add the repository to Apt sources:&lt;/span&gt;
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/docker.sources &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; /etc/os-release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;UBUNTU_CODENAME&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;$VERSION_CODENAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code allows us to install Docker under apt. Let's do so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's verify it's working. &lt;strong&gt;Always use &lt;code&gt;sudo&lt;/code&gt; to run docker commands&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status docker
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run hello-world

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuring AWS Systems Manager
&lt;/h3&gt;

&lt;p&gt;AWS Systems Manager (SSM) agent should already be installed on the instance; however, in case it isn't, you can use &lt;code&gt;snap&lt;/code&gt; to install it. We'll then run a &lt;code&gt;systemctl&lt;/code&gt; command to start the service. &lt;/p&gt;

&lt;p&gt;SSM is a comprehensive management service that provides a unified user interface for tracking and resolving operational issues across AWS and hybrid cloud environments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;snap &lt;span class="nb"&gt;install &lt;/span&gt;amazon-ssm-agent &lt;span class="nt"&gt;--classic&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;snap.amazon-ssm-agent.amazon-ssm-agent.service
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start snap.amazon-ssm-agent.amazon-ssm-agent.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can verify it's running by checking the status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status snap.amazon-ssm-agent.amazon-ssm-agent.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back inside the console open up the EC2 instance. Use the &lt;code&gt;Actions&lt;/code&gt; menu to modify the attached IAM role.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxyifhbvyi31ihf44trx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxyifhbvyi31ihf44trx.png" alt="EC2 Actions menu showing the option to modify the IAM role"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure to attach the AmazonSSMManagedInstanceCore and AmazonEC2ContainerRegistryReadOnly policies. The AmazonSSMManagedInstanceCore policy allows the EC2 instance to be managed by AWS Systems Manager, enabling remote access and command execution without SSH. The AmazonEC2ContainerRegistryReadOnly policy grants the instance permission to pull Docker images from &lt;a href="https://aws.amazon.com/ecr/" rel="noopener noreferrer"&gt;Amazon Elastic Container Registry (ECR)&lt;/a&gt; repositories.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhppx5wjiq0abp10yuac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhppx5wjiq0abp10yuac.png" alt="IAM role with AmazonSSMManagedInstanceCore and AmazonEC2ContainerRegistryReadOnly policies attached"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the new role and click the &lt;code&gt;Update IAM role&lt;/code&gt; button to complete the process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvfviw8hn0d7was7iaia7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvfviw8hn0d7was7iaia7.png" alt="Update IAM role confirmation screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like, you can check Systems Manager in the console to see the EC2 instance connected. Search for Systems Manager -&amp;gt; Fleet Manager  and you'll see the instance connected. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6mwxv1vid3t3snf8iciy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6mwxv1vid3t3snf8iciy.png" alt="Systems Manager Fleet Manager showing the EC2 instance connected"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the deploy directory and ECR
&lt;/h3&gt;

&lt;p&gt;For our app deployment, we'll be hosting our images on ECR. We'll need a script that will be triggered by SSM to pull the &lt;code&gt;latest&lt;/code&gt; tag and run it.  &lt;/p&gt;

&lt;p&gt;To do this we'll create a new directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /opt/nuxt-app
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;ubuntu:ubuntu /opt/nuxt-app
&lt;span class="nb"&gt;cd&lt;/span&gt; /opt/nuxt-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's create the &lt;code&gt;docker-compose.yml&lt;/code&gt; file inside the &lt;code&gt;/opt/nuxt-app&lt;/code&gt; folder. Don't worry about the image, we'll replace that later. Also go ahead and create an empty &lt;code&gt;.env&lt;/code&gt; file now — Docker Compose will fail if the &lt;code&gt;env_file&lt;/code&gt; path doesn't exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; /opt/nuxt-app/.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;nuxt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;REPLACE_LATER&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1:3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/opt/nuxt-app/.env&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll need to install the AWS CLI so we can pull down the deployed image from ECR. Install the latest version from the &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;docs&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;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;unzip
curl &lt;span class="s2"&gt;"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"awscliv2.zip"&lt;/span&gt;
unzip awscliv2.zip
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./aws/install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As always, make sure it works by running the commands below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--version&lt;/span&gt;
aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will verify that AWS CLI is working and that the permissions we set earlier in the IAM policy are there.&lt;/p&gt;

&lt;p&gt;Let's now configure ECR!&lt;/p&gt;

&lt;p&gt;Inside the AWS console head to ECR → Repositories → Create repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjcjoef2hup21wjbopmv6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjcjoef2hup21wjbopmv6.png" alt="ECR console showing the Create repository page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add a name and create it. In this case I'll use the postfix &lt;code&gt;ec2/host&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt9rsiv4ettew8pe9ygj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt9rsiv4ettew8pe9ygj.png" alt="ECR repository created with the ec2/host name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make sure to write down the full ECR repository name for later.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting GitHub with OIDC
&lt;/h3&gt;

&lt;p&gt;Now let's set up an OIDC provider for GitHub. Later we'll create a GitHub Action that will need access to our AWS account to work. To get more information on how OIDC providers work in GitHub, feel free to check out this &lt;a href="https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-aws" rel="noopener noreferrer"&gt;guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Inside the AWS console head to IAM → Identity Providers → Add provider.&lt;/p&gt;

&lt;p&gt;On this page make sure to select &lt;code&gt;OpenID Connect&lt;/code&gt;, use the URL &lt;code&gt;https://token.actions.githubusercontent.com&lt;/code&gt; and the audience as &lt;code&gt;sts.amazonaws.com&lt;/code&gt;. It should look like the screenshot below! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2doo6e2iagdfkgy22tou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2doo6e2iagdfkgy22tou.png" alt="IAM Identity Provider setup with OpenID Connect for GitHub Actions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's create a new role for this.&lt;/p&gt;

&lt;p&gt;Inside the AWS console head to IAM → Roles → Create role.&lt;/p&gt;

&lt;p&gt;Create a new IAM role connecting to this provider. Choose &lt;code&gt;Web identity&lt;/code&gt; and make sure to use the new identity provider you just created. Type in the GitHub organization and repository. If you like, you can also select the branch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1xyqp7es4l14vkix1oh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1xyqp7es4l14vkix1oh.png" alt="IAM role creation with Web identity provider and GitHub organization and repo fields"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click next a few times and create the role! We aren't done yet though — we need to make sure this role has permissions to ECR so the GitHub Action can trigger our deployment workflow.&lt;/p&gt;

&lt;p&gt;Under permissions click the &lt;code&gt;Create inline policy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Favmrm80ggttxig8g8vq1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Favmrm80ggttxig8g8vq1.png" alt="IAM role permissions tab with Create inline policy button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select JSON and copy and paste this in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ECRAuth"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ecr:GetAuthorizationToken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ECRPush"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&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="s2"&gt;"ecr:BatchCheckLayerAvailability"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ecr:CompleteLayerUpload"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ecr:InitiateLayerUpload"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ecr:PutImage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ecr:UploadLayerPart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ecr:BatchGetImage"&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;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:ecr:YOUR-REGION:YOUR-ACCOUNT-ID:repository/YOUR-REPO-NAME"&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;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SSMRunCommand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ssm:SendCommand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&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="s2"&gt;"arn:aws:ssm:*:*:document/AWS-RunShellScript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:ec2:YOUR-REGION:YOUR-ACCOUNT-ID:instance/i-xxxxxxxxxxxxxxxxx"&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="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;Make sure to update the instance with your instance ID. You can find that in your EC2 console. It starts with &lt;code&gt;i-&lt;/code&gt;. You also need to update YOUR-REGION (e.g. us-east-1) and &lt;code&gt;YOUR-ACCOUNT-ID&lt;/code&gt; with your account ID. Also update &lt;code&gt;YOUR-REPO-NAME&lt;/code&gt; with your ECR repo name. In my case I called it &lt;code&gt;ec2/host&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Setting up for production&lt;/strong&gt;&lt;br&gt;
We're using &lt;code&gt;AWS-RunShellScript&lt;/code&gt; here which lets the CI pipeline run any shell command on your instance. That's great for a tutorial, however a compromised GitHub Actions workflow or malicious PR could achieve remote code execution on your host. In production, create a &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-doc-syntax.html" rel="noopener noreferrer"&gt;custom SSM document&lt;/a&gt; that only runs your deploy script. That way even if the GitHub role is compromised, it can only trigger the specific deployment, not arbitrary commands on your box. A minimal custom document looks like this:&lt;/p&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="nl"&gt;"schemaVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deploy Nuxt app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mainSteps"&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;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aws:runShellScript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"inputs"&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;"runCommand"&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="s2"&gt;"sudo bash /opt/nuxt-app/deploy.sh"&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="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;p&gt;Then reference your custom document name in the GitHub Action instead of &lt;code&gt;AWS-RunShellScript&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Click create, name it &lt;code&gt;github-deploy-nuxt&lt;/code&gt;, and you are good to go.&lt;/p&gt;

&lt;p&gt;Make sure to copy the ARN for later! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcflxkbp9qj1uxulqvy5t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcflxkbp9qj1uxulqvy5t.png" alt="IAM role summary showing the ARN to copy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now inside our GitHub repo let's add the GitHub variables.&lt;/p&gt;

&lt;p&gt;Open up your GitHub repo and head to Settings → Secrets and Variables → Actions → Variables.&lt;/p&gt;

&lt;p&gt;Add five new environment variables. These will be used in our GitHub Action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnth1qntd4qlqhkjymfe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnth1qntd4qlqhkjymfe.png" alt="GitHub Actions variables page with AWS environment variables configured"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add each variable one by one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS_REGION  us-west-2
AWS_ACCOUNT_ID  your account id
ECR_REPO    ec2/host
INSTANCE_ID i-xxxxxx
AWS_DEPLOY_ROLE_ARN role ARN from above
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding the GitHub Action and Dockerfile
&lt;/h3&gt;

&lt;p&gt;For the deployment to work successfully we need to set up a GitHub Action that will deploy our app. Let's do that now.&lt;/p&gt;

&lt;p&gt;In your repo add a new &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt; file. This will be run whenever a push occurs to main.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Nuxt SSR&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS credentials&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;role-to-assume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ vars.AWS_DEPLOY_ROLE_ARN }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ vars.AWS_REGION }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to ECR&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/amazon-ecr-login@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push Docker image&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;IMAGE_URI=${{ vars.AWS_ACCOUNT_ID }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com/${{ vars.ECR_REPO }}&lt;/span&gt;

          &lt;span class="s"&gt;docker build -t $IMAGE_URI:prod .&lt;/span&gt;
          &lt;span class="s"&gt;docker push $IMAGE_URI:prod&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy via SSM&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;COMMAND_ID=$(aws ssm send-command \&lt;/span&gt;
            &lt;span class="s"&gt;--instance-ids "${{ vars.INSTANCE_ID }}" \&lt;/span&gt;
            &lt;span class="s"&gt;--document-name "AWS-RunShellScript" \&lt;/span&gt;
            &lt;span class="s"&gt;--parameters '{"commands":["sudo bash /opt/nuxt-app/deploy.sh"]}' \&lt;/span&gt;
            &lt;span class="s"&gt;--query "Command.CommandId" --output text)&lt;/span&gt;

          &lt;span class="s"&gt;aws ssm wait command-executed \&lt;/span&gt;
            &lt;span class="s"&gt;--command-id "$COMMAND_ID" \&lt;/span&gt;
            &lt;span class="s"&gt;--instance-id "${{ vars.INSTANCE_ID }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This action will assume our AWS credentials based on the GitHub role we just created. It will then build a Docker image and push it to ECR. We'll then trigger SSM to run our script that will complete the deployment.&lt;/p&gt;

&lt;p&gt;For us to build our Docker image, we'll need a Dockerfile in the root of our repo. Head to the repo and in the root add a new &lt;code&gt;Dockerfile&lt;/code&gt;. This will build our Nuxt application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# --- build stage ---&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-bookworm-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="c"&gt;# --- runtime stage ---&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-bookworm-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runtime&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/.output ./.output&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", ".output/server/index.mjs"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll also need a &lt;code&gt;.dockerignore&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules
.nuxt
.output
.git
.gitignore
.env
.env.*
*.pem
*.md
.vscode
.idea

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we get too far, let's test it out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing the deployment
&lt;/h3&gt;

&lt;p&gt;We are going to test our deployment, but first let's temporarily open up port 3000 on our EC2 host. We can then connect to the IP address later to see if our deployment works.&lt;/p&gt;

&lt;p&gt;Inside the AWS console head to EC2 → Security Groups → Inbound rules.&lt;/p&gt;

&lt;p&gt;Click on the security group.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F907q65rlyxpvik8i2n2u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F907q65rlyxpvik8i2n2u.png" alt="EC2 security group showing inbound rules"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Edit inbound rules and add your IP address on port 3000 on &lt;code&gt;Custom&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2vmiv4u1y1uoi9kwd9xf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2vmiv4u1y1uoi9kwd9xf.png" alt="Inbound rules editor with custom TCP rule for port 3000 restricted to your IP"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's head back to the Docker Compose file again. This is important — before you push anything to GitHub, update the &lt;code&gt;docker-compose.yml&lt;/code&gt; with your real ECR image URI. If you skip this step the deploy will fail because Docker won't know what image to pull. Add in the ECR image name and set the port to 3000. Make sure to add the &lt;code&gt;:prod&lt;/code&gt; tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;nuxt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;account-id&amp;gt;.dkr.ecr.us-east-1.amazonaws.com/ec2/host:prod&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/opt/nuxt-app/.env&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For my app I use several secrets; however, as you can see from earlier, our Docker ignore file excludes .env files (for good reason). Create a new &lt;code&gt;.env&lt;/code&gt; file with the secrets. This will be injected into our docker image later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /opt/nuxt-app/.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next is the deploy script!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano /opt/nuxt-app/deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The GitHub action will run this on every deploy to main.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; /opt/nuxt-app

aws ecr get-login-password &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--region&lt;/span&gt; YOUR_REGION | &lt;span class="se"&gt;\&lt;/span&gt;
docker login &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--password-stdin&lt;/span&gt; YOUR_ACCOUNT_ID.dkr.ecr.YOUR_REGION.amazonaws.com

docker compose pull
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
docker image prune &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Don't use .env files in production&lt;/strong&gt;&lt;br&gt;
Plaintext &lt;code&gt;.env&lt;/code&gt; files on disk have no rotation, no audit trail, and no access control. For production, use &lt;a href="https://aws.amazon.com/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt; or &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html" rel="noopener noreferrer"&gt;Systems Manager Parameter Store&lt;/a&gt; (SecureString) to manage application secrets and pull them at runtime. The &lt;code&gt;.env&lt;/code&gt; approach shown here is suitable for development and tutorials only. You could also set up a blue/green deployment — spin up the new container, health-check it, then swap traffic only if healthy. For the purpose of this tutorial though, we'll keep it simpler.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Replace the &lt;code&gt;YOUR_ACCOUNT_ID&lt;/code&gt; with your account id and &lt;code&gt;YOUR_REGION&lt;/code&gt; with your region.&lt;/p&gt;

&lt;p&gt;Then make it executable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are finally ready to try it all out! Push all your changes from your repo to GitHub and check the &lt;code&gt;Actions&lt;/code&gt; tab at the top and see it deploying!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnu3d6gtq82uz8avabshi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnu3d6gtq82uz8avabshi.png" alt="GitHub Actions tab showing a successful deployment workflow run"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Troubleshooting&lt;/strong&gt;&lt;br&gt;
If you see errors at this point, you'll need to double-check your GitHub role and that everything is connected. This might be a good time to use Kiro to help troubleshoot the problem!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can check if everything works by loading up port 3000 with the public IP address of the EC2 instance.&lt;/p&gt;

&lt;p&gt;Go to &lt;code&gt;http://&amp;lt;your-ec2-ip-address&amp;gt;:3000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Congrats 🥳! You now have a working deployment pipeline and your container is working!&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up CloudFront, SSL, and nginx
&lt;/h3&gt;

&lt;p&gt;Our pipeline is working; however, we need to revert the ports change we made in the &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Change back the &lt;code&gt;/opt/nuxt-app/docker-compose.yml&lt;/code&gt; to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;nuxt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;xxxx.dkr.ecr.us-east-1.amazonaws.com/ec2/host:prod&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1:3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/opt/nuxt-app/.env&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then apply it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /opt/nuxt-app
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Remove port 3000 from your security group now.&lt;/strong&gt; It exposes your raw application server to the internet, bypassing nginx. This was only needed for testing — don't leave it open.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's see if it works on the normal port 80.&lt;/p&gt;

&lt;p&gt;Open up &lt;code&gt;http://&amp;lt;your-ec2-ip-address&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If all goes well you should be seeing your website! 🎉&lt;/p&gt;

&lt;p&gt;But now we need CloudFront to add a Content Delivery Network (CDN) that will cache our static assets globally and improve performance for users worldwide. CloudFront also provides additional security features and helps protect our origin server from direct access.&lt;/p&gt;

&lt;p&gt;Let's assume you already have a domain in &lt;a href="https://aws.amazon.com/route53/" rel="noopener noreferrer"&gt;Route53&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Let's request a public certificate so we can add in SSL.&lt;/p&gt;

&lt;p&gt;In the AWS Console head to Certificate Manager → switch region to us-east-1 → Request certificate →  Request a public certificate .&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsnvk0245n94mz80p73u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsnvk0245n94mz80p73u.png" alt="Certificate Manager request page for a public SSL certificate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then add your fully qualified domain (e.g. yourdomain.com). And add another for www (e.g. &lt;a href="http://www.yourdomain.com" rel="noopener noreferrer"&gt;www.yourdomain.com&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Leave everything else defaulted and click &lt;code&gt;Request&lt;/code&gt;. Wait a few minutes for the certificate to be issued.&lt;/p&gt;

&lt;p&gt;Inside the AWS Console head to CloudFront and create a new distribution.&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Create Distribution&lt;/code&gt;. Choose the free plan. Make sure to enter the distribution name, the domain name, and the &lt;code&gt;Domain to serve&lt;/code&gt; (www). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2hmtlrq8gnylu3a22awp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2hmtlrq8gnylu3a22awp.png" alt="CloudFront Create Distribution page with distribution name, domain, and www settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next page choose &lt;code&gt;Other&lt;/code&gt;. For origin, type in the public DNS of the EC2 instance. You may need to go back to grab it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9l3w301rwe2f298277y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9l3w301rwe2f298277y.png" alt="CloudFront origin settings with EC2 public DNS as the origin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the origin settings to customize origin settings. Then set an X-Origin-Verify header with a random secret you create.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why this header matters&lt;/strong&gt;&lt;br&gt;
The CloudFront Origin Header is extremely important. This ensures that your origin server can only be accessed through CloudFront and not directly from the internet, providing an additional layer of security.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Use HTTPS to your origin in production&lt;/strong&gt;&lt;br&gt;
We're using HTTP for the origin protocol here since CloudFront handles HTTPS for your users. However, the X-Origin-Verify shared secret is transmitted in plaintext over this connection. Even with the security group locked to the CloudFront prefix list, traffic between CloudFront edge nodes and your EC2 instance traverses the public internet and could be intercepted. For production workloads, install a certificate on your instance (a self-signed cert works fine since CloudFront doesn't validate origin certs by default, or use &lt;a href="https://certbot.eff.org/" rel="noopener noreferrer"&gt;Certbot&lt;/a&gt; for a free Let's Encrypt certificate), switch nginx to listen on 443, update CloudFront's origin protocol to HTTPS-only, and move your prefix list security group rule from port 80 to 443. This encrypts the entire path and keeps the shared header as a defense-in-depth measure rather than your primary access control.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0brv0gd0mscbfkjzbbup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0brv0gd0mscbfkjzbbup.png" alt="Custom origin header configuration with X-Origin-Verify secret"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For cache settings, choose "CachingDisabled" as we'll configure specific caching behaviors for different content types after creating the distribution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk9fjarva3lhg4idcgu0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk9fjarva3lhg4idcgu0x.png" alt="CloudFront cache settings with CachingDisabled selected"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the Web Application Firewall (WAF) settings, don't make any additional changes.&lt;/p&gt;

&lt;p&gt;On the settings page, select the SSL certificate you created earlier from the dropdown menu. Make sure both your domain and www subdomain are covered.&lt;/p&gt;

&lt;p&gt;Review all your configuration settings and click "Create distribution". CloudFront will take several minutes to deploy globally — you'll see the status change from "Deploying" to "Enabled" when it's ready.&lt;/p&gt;

&lt;p&gt;After it's enabled, add a new behavior. Click &lt;code&gt;Create behavior&lt;/code&gt;. Fill out the &lt;code&gt;Path pattern&lt;/code&gt; as &lt;code&gt;/_nuxt/*&lt;/code&gt;, set the &lt;code&gt;Origin and origin groups&lt;/code&gt; dropdown to the EC2 instance. Make sure to set the &lt;code&gt;Viewer protocol policy&lt;/code&gt; to &lt;code&gt;Redirect HTTP to HTTPS&lt;/code&gt; and set the &lt;code&gt;Allowed HTTP methods&lt;/code&gt; to &lt;code&gt;GET, HEAD&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It should look like this at the end&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscnwoyttnu5og8j2el48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscnwoyttnu5og8j2el48.png" alt="CloudFront behavior for /_nuxt/* path pattern with HTTPS redirect and GET HEAD methods"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that CloudFront is in place, let's update the nginx configuration with the new secret key we created earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/nuxt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$http_x_origin_verify&lt;/span&gt; &lt;span class="s"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"REPLACE_WITH_SECRET")&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;"upgrade"&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;p&gt;Replace the &lt;code&gt;REPLACE_WITH_SECRET&lt;/code&gt; with the variable you set up in CloudFront. &lt;/p&gt;

&lt;p&gt;Then reload nginx.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if you test the origin you should get a 403 forbidden.&lt;/p&gt;

&lt;p&gt;Go to &lt;code&gt;http://&amp;lt;your-ec2-ip-address&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lock down port 80&lt;/strong&gt;&lt;br&gt;
For an extra layer of security, go to your EC2 security group and delete the existing port 80 inbound rule, then add a new one with the source set to the AWS-managed prefix list &lt;code&gt;com.amazonaws.global.cloudfront.origin-facing&lt;/code&gt;. This restricts port 80 so only CloudFront's network can reach your instance. Combined with the origin verify header, you now have two layers of protection on your origin.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🔥 We are getting one step closer!&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting your domain with Route 53
&lt;/h3&gt;

&lt;p&gt;Now you need to connect your CloudFront distribution to your domain through Route53.&lt;/p&gt;

&lt;p&gt;Go to Route53 in the AWS console and navigate to your hosted zone for your domain. Click "Create record" to add a new A record that will point your domain to the CloudFront distribution.&lt;/p&gt;

&lt;p&gt;Make sure you add a record. Choose &lt;code&gt;Alias&lt;/code&gt; and then choose &lt;code&gt;Alias to CloudFront distribution&lt;/code&gt; from the dropdown. Select your CloudFront distribution from the list. Leave the Record name blank for the root domain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrhdvp4gbx35ic6ed5b2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrhdvp4gbx35ic6ed5b2.png" alt="Route 53 A record aliased to the CloudFront distribution for the root domain"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add another A record, but this time for the &lt;code&gt;www&lt;/code&gt; subdomain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8syomaa6qgq6499hv7q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8syomaa6qgq6499hv7q.png" alt="Route 53 A record for the www subdomain aliased to CloudFront"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It might take a few minutes for DNS propagation to complete, but your site should be accessible at your domain shortly after creating the A records.&lt;/p&gt;

&lt;p&gt;You can test the CloudFront distribution directly using its domain name while waiting for DNS to propagate. Once everything is working, visit your domain and you should see your site served securely over HTTPS!&lt;/p&gt;

&lt;p&gt;Go to &lt;code&gt;https://&amp;lt;your-domain&amp;gt;&lt;/code&gt; 🎇&lt;/p&gt;

&lt;p&gt;You can even open Chrome DevTools and check the Network tab to see CloudFront cache hits in the response headers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpf7mqnnp30ghru21c72l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpf7mqnnp30ghru21c72l.png" alt="Chrome DevTools Network tab showing CloudFront cache hit headers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding cache behaviors
&lt;/h3&gt;

&lt;p&gt;If you'd like, we can add additional caching to our CloudFront distribution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/favicon.ico&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/robots.txt&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;&lt;code&gt;/sitemap.xml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/images/*&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these files can have their own caching rules. Static assets can be cached for longer periods, while you might want to disable caching entirely for server-side rendered (SSR) routes in Nuxt to ensure dynamic content is always fresh.&lt;/p&gt;

&lt;p&gt;To add these behaviors, go back to your CloudFront distribution and create new behaviors for each path pattern, adjusting the cache settings based on how frequently the content changes.&lt;/p&gt;

&lt;p&gt;Once everything is working properly, it's also a good security practice to remove SSH access from your EC2 instance's security group. Since we set up AWS Systems Manager earlier, you can still connect to your instance anytime through the SSM Session Manager in the AWS console — no SSH key needed, no port 22 open to the internet. Just head to your EC2 security group, delete the inbound rule for port 22, and save.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production hardening
&lt;/h2&gt;

&lt;p&gt;What we've built so far is a solid working deployment — but it's tutorial-grade. Before serving real user traffic, here are the things you'll want to tighten up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secrets management.&lt;/strong&gt; Replace the flat &lt;code&gt;.env&lt;/code&gt; file with &lt;a href="https://aws.amazon.com/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt; or &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html" rel="noopener noreferrer"&gt;SSM Parameter Store&lt;/a&gt; (SecureString). This gives you rotation, audit trails, and fine-grained access control instead of plaintext on disk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Origin encryption.&lt;/strong&gt; Install a TLS certificate on your instance (self-signed works since CloudFront doesn't validate origin certs by default, or use &lt;a href="https://certbot.eff.org/" rel="noopener noreferrer"&gt;Certbot&lt;/a&gt; for a free Let's Encrypt certificate), switch nginx to listen on 443, and set CloudFront's origin protocol to HTTPS-only. Then move your prefix list security group rule from port 80 to 443. This encrypts the hop between CloudFront and your origin so the X-Origin-Verify header can't be sniffed in transit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lock down SSM.&lt;/strong&gt; Replace &lt;code&gt;AWS-RunShellScript&lt;/code&gt; with a &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-doc-syntax.html" rel="noopener noreferrer"&gt;custom SSM document&lt;/a&gt; that only runs your deploy script. This limits the blast radius if your GitHub Actions role is ever compromised. See the example earlier in this post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security group hygiene.&lt;/strong&gt; Remove SSH (port 22) access entirely — you have SSM Session Manager for shell access. Restrict port 80/443 ingress to the &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/LocationsOfEdgeServers.html" rel="noopener noreferrer"&gt;CloudFront managed prefix list&lt;/a&gt; (&lt;code&gt;com.amazonaws.global.cloudfront.origin-facing&lt;/code&gt;) so only CloudFront can reach your origin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoring and detection.&lt;/strong&gt; Set up &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html" rel="noopener noreferrer"&gt;CloudWatch alarms&lt;/a&gt; for CPU, memory, and disk usage. Enable &lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html" rel="noopener noreferrer"&gt;VPC Flow Logs&lt;/a&gt; to capture network traffic metadata. Consider enabling &lt;a href="https://aws.amazon.com/waf/" rel="noopener noreferrer"&gt;AWS WAF&lt;/a&gt; on your CloudFront distribution to filter malicious requests. Without these, a breach or resource issue could go undetected indefinitely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero-downtime deploys.&lt;/strong&gt; Set up a blue/green deployment — spin up the new container, health-check it, then swap traffic only if healthy. Your deploy script can pull the new image, start it on a different port, verify it responds, then update nginx and stop the old container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scaling.&lt;/strong&gt; If you outgrow a single instance, add an &lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/auto-scaling-groups.html" rel="noopener noreferrer"&gt;Auto Scaling Group&lt;/a&gt; with an &lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html" rel="noopener noreferrer"&gt;Application Load Balancer&lt;/a&gt; to distribute traffic across multiple instances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;If you're done experimenting and want to tear everything down, here's the order I'd go in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Head to Route 53 and delete the A records you created for your domain and &lt;a href="http://www" rel="noopener noreferrer"&gt;www&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Go to CloudFront, disable your distribution, wait about 5 to 10 minutes, then delete it.&lt;/li&gt;
&lt;li&gt;Delete the ACM certificate in Certificate Manager.&lt;/li&gt;
&lt;li&gt;Head to ECR and delete your repository.&lt;/li&gt;
&lt;li&gt;Terminate your EC2 instance and delete the security group and key pair you created.&lt;/li&gt;
&lt;li&gt;Clean up the IAM roles and the OIDC identity provider for GitHub.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Going in this order avoids dependency issues, CloudFront needs to be disabled before you can delete the certificate, and Route 53 records need to be removed before disabling the distribution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;Remember the problem we started with? Too many subscriptions, serverless timeouts, and no real control over your stack. We just solved all of that with a single EC2 instance.&lt;/p&gt;

&lt;p&gt;Here's what we built: a t3.micro instance running Ubuntu with nginx as a reverse proxy, Docker for containerization, a CI/CD pipeline with GitHub Actions and ECR, CloudFront as our CDN with SSL via Certificate Manager, and Route 53 pointing our domain to it all. &lt;/p&gt;

&lt;p&gt;This gives you a working foundation you can build on. Check the Production Hardening section above to close the gaps before serving real traffic — but the hard part is done. You own your stack, you understand every piece of it, and you can evolve it on your terms.&lt;/p&gt;

&lt;p&gt;If this helped you out, drop a comment below and let me know what you're deploying. Until next time!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>webhost</category>
      <category>webdev</category>
    </item>
    <item>
      <title>From Chat to Specs: A Deep Dive into AI-Assisted Development with Kiro</title>
      <dc:creator>Erik Hanchett</dc:creator>
      <pubDate>Tue, 15 Jul 2025 16:57:38 +0000</pubDate>
      <link>https://forem.com/kirodotdev/from-chat-to-specs-a-deep-dive-into-ai-assisted-development-with-kiro-1415</link>
      <guid>https://forem.com/kirodotdev/from-chat-to-specs-a-deep-dive-into-ai-assisted-development-with-kiro-1415</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally &lt;a href="https://kiro.dev/blog/from-chat-to-specs-deep-dive/" rel="noopener noreferrer"&gt;published&lt;/a&gt; on the Kiro blog from Ryan Yanchuleff.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As developers, we've all been there. You have a brilliant idea for a feature or application, you fire up your favorite AI coding assistant, and then... you spend the next hour going back and forth, refining requirements, clarifying edge cases, and watching your context window fill up with exploratory conversations before you even write a single line of code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kiro.dev" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;, a new IDE, fundamentally changes how we approach AI-assisted development through Spec-Driven Development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations of current AI coding assistants
&lt;/h2&gt;

&lt;p&gt;Limitations of current AI coding assistants tend to follow a predictable and inefficient pattern. When a developer provides a high-level prompt, the AI immediately jumps into code generation, often before fully understanding the requirements. This premature action leads to a cycle where the developer must repeatedly clarify their intentions with "actually, I meant..." statements, as the initial requirements weren't sufficiently clear. As this exploratory dialogue continues, the context window becomes increasingly cluttered with back-and-forth discussions, leaving limited space for the final code generation. This constrained context space ultimately impacts the quality and completeness of the final output, making the entire process less efficient than it could be. This approach treats the LLM as a code generator first, when it should be considered a thinking partner throughout the entire development lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spec-driven development: Bridging design intent and implementation
&lt;/h2&gt;

&lt;p&gt;If you're working on a challenging feature, Kiro serves as your intelligent sounding board to help you understand your codebase, define your problem clearly, and reach a quality solution efficiently. You can collaborate with Kiro on creating concise specifications that include clear requirements, system architecture, tech stack considerations, and implementation approach. Kiro helps make all requirements and constraints explicit, then uses these specifications as context to complete tasks with fewer iterations and higher accuracy. This is the power of spec-driven development. Let's dive deeper into some of the key benefits of Kiro's approach:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Understand your existing codebase
&lt;/h3&gt;

&lt;p&gt;Before starting new development, Kiro analyzes your existing code and generates three foundational documents: structure.md (codebase architecture), tech.md (technical stack and patterns), and product.md (business context and requirements). This gives you and your team a clear baseline understanding that informs all subsequent specification work. Existing codebases can now take advantage of this new paradigm.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Analyze and plan your project
&lt;/h3&gt;

&lt;p&gt;When you provide a project prompt in spec mode, Kiro's AI doesn't immediately start coding. Instead, it performs deep analysis to understand your requirements, identifies potential challenges, and creates comprehensive planning documents.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Generate comprehensive planning documentation
&lt;/h3&gt;

&lt;p&gt;From a simple prompt, Kiro creates detailed specification files including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Requirements Analysis&lt;/strong&gt; - Breaking down your prompt into specific, actionable requirements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Technical Design&lt;/strong&gt; - Architecture decisions, technology choices, and implementation approach&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Task Breakdown&lt;/strong&gt; - Granular development tasks with clear acceptance criteria&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Collaborate with AI effectively
&lt;/h3&gt;

&lt;p&gt;Kiro saves specification files in your project directory as readable markdown files. You can review, edit, and refine them before any code is written. This creates natural checkpoints for collaboration with team members or stakeholders.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Maximize coding context and efficiency
&lt;/h3&gt;

&lt;p&gt;When it's finally time to write code, Kiro references these specification files rather than cluttering your context window with exploratory conversation. This means maximum context space is available for the actual coding task.&lt;/p&gt;

&lt;h2&gt;
  
  
  The power of spec-driven development
&lt;/h2&gt;

&lt;p&gt;Spec-driven development delivers key advantages that fundamentally improve how teams design, build, and maintain software. Rather than treating planning as overhead, it becomes your competitive advantage. Here's how this approach transforms the development process:&lt;/p&gt;

&lt;h3&gt;
  
  
  Catch problems before they're expensive
&lt;/h3&gt;

&lt;p&gt;Rather than discovering requirements issues mid-development, Kiro identifies and resolves ambiguities upfront. This prevents costly rewrites and provides alignment before coding begins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stay in control of your project's direction
&lt;/h3&gt;

&lt;p&gt;The specification phase creates natural pause points where humans can review, modify, and approve the direction before resources are invested in implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Iterate without losing your progress
&lt;/h3&gt;

&lt;p&gt;If you make a mistake in defining your requirements, no problem. You can modify the specification files and regenerate the implementation plan without losing your entire conversation history.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep your AI focused on what matters
&lt;/h3&gt;

&lt;p&gt;By externalizing the planning phase to files, Kiro keeps the active context focused on the immediate coding task, leading to higher quality code generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable seamless team collaboration
&lt;/h3&gt;

&lt;p&gt;Specification files serve as living documentation that team members can review, comment on, and contribute to using standard development workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build institutional knowledge
&lt;/h3&gt;

&lt;p&gt;Every decision and requirement is documented, creating a clear audit trail of why certain technical choices were made and preserving context for future team members.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s see Kiro specs in action
&lt;/h2&gt;

&lt;p&gt;The best way to understand spec-driven development is to see it in practice. Whether you're starting fresh or working with an existing codebase, Kiro's systematic approach enables you to build on a solid foundation. Here's how a typical workflow unfolds, from initial concept to implementation-ready specifications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbt4gugjxl718ti2j6q7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbt4gugjxl718ti2j6q7.png" alt="Figure 1: Kiro uses a 'Spec-Driven Development' mode to produce detailed outputs with requirements, designs, and tasks." width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Initiating your project
&lt;/h3&gt;

&lt;p&gt;Before diving into new features, establish context for your project:&lt;/p&gt;

&lt;p&gt;User: "Set up steering for this project"&lt;/p&gt;

&lt;p&gt;Kiro analyzes your existing codebase and generates three foundational documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;structure.md&lt;/strong&gt; - Current architecture, key components, and code organization&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;tech.md&lt;/strong&gt; - Technology stack, patterns, and technical constraints&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;product.md&lt;/strong&gt; - Business context, existing features, and user workflows&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives you a clear baseline understanding of what you're building upon.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Generating your project specs
&lt;/h3&gt;

&lt;p&gt;Now start laying out the details for the project that you want to build.&lt;/p&gt;

&lt;p&gt;User: "I want to build a task management app for small teams with real-time collaboration features"&lt;/p&gt;

&lt;p&gt;This is where the magic of spec-driven development becomes apparent. Rather than jumping straight into framework selection or database design, Kiro takes a step back to fully understand what you're trying to accomplish. It considers your prompt in the context of the steering documents, identifying how this new feature fits within your existing architecture and constraints.&lt;/p&gt;

&lt;p&gt;Kiro creates a series of documents in sequential order, building from requirements through to actionable tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;requirements.md&lt;/strong&gt; - Detailed feature breakdown including user stories and acceptance criteria&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;design.md&lt;/strong&gt; - Architecture and technology decisions including frameworks, architecture diagrams, and structure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;tasks.md&lt;/strong&gt; - Development phases and tasks to be executed in a sequential order&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdf6kbm8hiu9sged5ohjv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdf6kbm8hiu9sged5ohjv.gif" alt="Figure 2: Kiro creates three main documents: requirements, design, and the task list&amp;lt;br&amp;gt;
" width="320" height="180"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Reviewing and refining
&lt;/h3&gt;

&lt;p&gt;You review the specifications, perhaps adding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Additional Requirements
- Integration with Slack for notifications
- Mobile-responsive design priority
- GDPR compliance considerations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Informed Development
&lt;/h3&gt;

&lt;p&gt;Now when Kiro begins coding, it references these comprehensive specifications rather than trying to infer requirements from conversation history. Every implementation decision is grounded in documented requirements and design choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  The future of development is here—and it starts in the specs
&lt;/h2&gt;

&lt;p&gt;Spec-driven development represents a shift from reactive coding to proactive specification that isn't just a workflow improvement—it's a fundamental evolution in how we partner with AI to build software. Instead of treating AI as a sophisticated autocomplete tool, spec-driven development positions it as your strategic thinking partner, helping you make better decisions before they become expensive to change. The result? Faster development cycles, higher quality code, fewer surprises, and documentation that actually stays current because it's integral to the process, not an afterthought. The next time you have a feature to build, try leading with specification instead of code. Your future self (and your teammates) will thank you for the clarity, and you might just discover that the best code is the code you plan before you write.&lt;/p&gt;

&lt;p&gt;Ready to experience the difference? Kiro is now available and free during preview with generous usage limits. &lt;a href="https://kiro.dev/downloads/" rel="noopener noreferrer"&gt;Download it and get started today!&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What I Learned from Vibe Coding</title>
      <dc:creator>Erik Hanchett</dc:creator>
      <pubDate>Wed, 26 Mar 2025 21:13:50 +0000</pubDate>
      <link>https://forem.com/erikch/what-i-learned-vibe-coding-30em</link>
      <guid>https://forem.com/erikch/what-i-learned-vibe-coding-30em</guid>
      <description>&lt;p&gt;You may have heard this term called “vibe coding”. It’s a new way of thinking about creating software using AI. It was coined by Andrej Karpathy, and he describes &lt;a href="https://x.com/karpathy/status/1886192184808149383" rel="noopener noreferrer"&gt;vibe coding&lt;/a&gt; as “Giving into the vibes, embrace exponentials, and forget that code even exists”. Karpathy uses an AI coding assistant and he adopts the philosophy of “accepting all”, assuming the AI coding assistant will write and fix the software he’s creating.&lt;/p&gt;

&lt;p&gt;While this way of coding sounds tempting, does it produce accurate enough results given today’s large language model limitations (LLM) and the overall changing landscape? Can you vibe code a complete application without issues? Can you have it create tests, and how does it handle inconsistencies with design? Is this a fad or a real long term strategy developers should learn and adopt? &lt;/p&gt;

&lt;p&gt;I had these same questions too, so I decided to create an experiment for myself. Without any prior knowledge I wanted to update my old, and frankly out of date &lt;a href="https://www.programwitherik.com/" rel="noopener noreferrer"&gt;personal blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fea8objvczxs4dfqukujc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fea8objvczxs4dfqukujc.png" alt="Picture of personal website" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’d vibe my way into a new, and hopefully better looking and functional website. The guard rails I set for myself was that &lt;strong&gt;I could NOT go into the code and make any changes&lt;/strong&gt;. Everything would have to be done by using a prompt and the coding assistant. Additionally, &lt;strong&gt;I would try my best to assume no knowledge of web development&lt;/strong&gt;. &lt;strong&gt;Spoiler&lt;/strong&gt;, I broke the second rule - we’ll chat more about that later. &lt;/p&gt;

&lt;p&gt;If you'd like to follow along, I created a full video of the experience!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/CXCLhA836yo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Let's jump into how to set up this experiment!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Full disclosure - I'm a Senior Developer Advocate for AWS!&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;For this experiment I am using &lt;a href="https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line.html?trk=1ad04439-1c50-4fdd-a845-d07d2655fe7a&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon Q CLI&lt;/a&gt; as my coding assistant. This command line interface (CLI) tool will be my agent, and I’ll prompt it to create my new personal website. I am a huge fan of CLI tools, and this will work perfectly for my experiment. If you'd like to follow along, Q CLI is &lt;a href="https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-installing.html?trk=9ff26abb-282e-49a3-82fe-d185d6344181&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;easy to install&lt;/a&gt; and supports several different operating systems, including my favorites MacOS, WSL and Linux. You can even set it up over SSH if needed. Also, don’t forget to sign up for &lt;a href="https://profile.aws.amazon.com/#/profile/details?trk=1ad04439-1c50-4fdd-a845-d07d2655fe7a&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Builder ID&lt;/a&gt;, it’s a free to sign-up and you’ll need it to sign in with the CLI.&lt;/p&gt;

&lt;p&gt;To speed things up a bit, I created a brand new application using Nuxt 3 with Tailwind CSS 4. I followed the procedures on the Nuxt installation &lt;a href="https://nuxt.com/docs/getting-started/installation" rel="noopener noreferrer"&gt;guide&lt;/a&gt; and then chose the Nuxt UI installation that includes &lt;a href="https://tailwindcss.com/docs/installation/using-vite" rel="noopener noreferrer"&gt;Tailwind CSS 4&lt;/a&gt;. I cleared out the starter files and began with a simple index.vue file to start. &lt;/p&gt;

&lt;p&gt;Here is my starting point.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fde3re6smiup0yt941ar1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fde3re6smiup0yt941ar1.png" alt="Chrome browser shows hello world" width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s worth mentioning that even though I used Nuxt which uses Vue.js, I’m almost certain this experiment would have worked the same way or even better with React, Angular, or even Svelte. &lt;/p&gt;

&lt;h2&gt;
  
  
  Building My Personal Website
&lt;/h2&gt;

&lt;p&gt;One thing I know about prompting, is that you should try to be specific. So I started with a basic prompt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1dv70tjx50gw22hk99ce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1dv70tjx50gw22hk99ce.png" alt="personal website prompt" width="800" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This prompt was explicitly written to generate a personal website, with spring colors, and animations. I then listed out that I wanted a blog, contact me, profile picture and bio page. For good measure I added in that I was using Tailwind CSS 4 and it was configured already. Although not necessary I always try to be polite with my prompts, and I was sure to include please and thank you. 😊&lt;/p&gt;

&lt;p&gt;After entering the prompt in and hitting enter I noticed that Q CLI read my directory and summarized what it was going to do. This was nice to see that things were going in the right direction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb672o54dj1o8kx1k639r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb672o54dj1o8kx1k639r.png" alt="summary of what Q CLI is doing" width="800" height="709"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point Q CLI started creating pages and I started blindly accepting ‘y’ for each change. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;It’s worth noting you can set Q CLI to accept all changes by using the /acceptall configuration or -a argument when you begin q chat. However, in this example I decided to just manually accept all changes.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Missing image
&lt;/h3&gt;

&lt;p&gt;After a few times of pressing ‘y’ on my keyboard, and accepting all the changes, I noticed a small issue with the &lt;code&gt;bio.vue&lt;/code&gt; page that was just created. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4nycmh78k22bf5a69si4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4nycmh78k22bf5a69si4.png" alt="profile placeholder error" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No problem! I told Q to fix the file, and it continued on it’s way!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fffv9cmv7au2ese91jizp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fffv9cmv7au2ese91jizp.png" alt="Image description" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Nuxt config update
&lt;/h3&gt;

&lt;p&gt;As I was continuing on confirming all changes a message came up to confirm a new update to the &lt;code&gt;nuxt.config.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fej489l830zeyfxeqd402.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fej489l830zeyfxeqd402.png" alt="Update to nuxt config file" width="800" height="812"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of my goals for this experiment was to assume no knowledge of web development and blindly "accept all" to all changes. However, to save time, and to fix an obvious issue, I stopped Q CLI when it tried to make modifications to the &lt;code&gt;nuxt.config.ts&lt;/code&gt; file. One of the primary responsibilities of this file is to add modules. Q CLI wanted to remove my &lt;code&gt;nuxt-ui&lt;/code&gt; module which would have broken all my Tailwind CSS in the app. &lt;/p&gt;

&lt;p&gt;Looking at the output, I believe Q CLI was confused and wanted to add in some PostCSS configuration, which is more often used with Tailwind CSS 3. To be safe here I typed &lt;code&gt;no&lt;/code&gt; here and reminded Q CLI that I was using a module and that I didn’t need this configuration. It continued on without issue, and did not update this file.&lt;/p&gt;

&lt;h3&gt;
  
  
  First iteration of website
&lt;/h3&gt;

&lt;p&gt;After a few more minutes of accepting more changes and fixing the minor mistakes mentioned earlier, my new personal website appeared! 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62rhzmy8udiex8vfb318.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62rhzmy8udiex8vfb318.png" alt="generated personal website" width="800" height="863"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It included most of the features I was expecting including a featured projects section. One thing that was not working was the blog section. So I went back and asked for it to create it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fin9nyfvjjfegk8ur21js.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fin9nyfvjjfegk8ur21js.png" alt="create blog prompt" width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within a few minutes it added an interactive blog. It even added in pagination, tags, and search functionality!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F458mf1ryo4eoskn5r9gt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F458mf1ryo4eoskn5r9gt.png" alt="blog screenshot" width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Overall it did a good job with the blog. It created separate dynamic routes for each page, and the tag filtering and search worked as well. The only concerns I had were with a few small design issues with mobile responsiveness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding more updates
&lt;/h3&gt;

&lt;p&gt;I continued on for another few minutes cleaning up the design. I asked Q CLI to update all the place holder images with images from Unsplash. I then had it clean up a few minor visual issues. This definitely made the site look a little cleaner.&lt;/p&gt;

&lt;p&gt;I then had it commit the changes into it’s own branch using git. It's important having an easy way to roll back or change between commits. As an added bonus from doing this, Q CLI started committing all my changes without me having to ask after every update. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa2n4dzwc896s3ssh69ee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa2n4dzwc896s3ssh69ee.png" alt="updated website" width="800" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;Testing is really important for any software you create. It’s a great way to ensure you don’t break anything while making updates to a code base. When working with AI coding assistants this is even more important. By creating tests AI agents can check that their changes didn’t break anything, and if they do fix the issue.&lt;/p&gt;

&lt;p&gt;To help test this philosophy I asked Q CLI to make some simple tests for each page in the application. With in seconds it installed vitest, and test-utils and began creating tests for each page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbcka52hb15t4bfnnuxjc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbcka52hb15t4bfnnuxjc.png" alt="testing screenshot" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing surprised me is that after creating all these tests, they didn’t pass. Q CLI went back and made changes 3-4 times until every test passed. I was impressed that it was able to self correct itself without additional prompting. Afterwards I quickly checked the tests out and they looked fine for this project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The goal was to vibe code, and this was a success. I was able to create a profesional, nice looking, personal website that featured a blog, contact page, and bio. I never once had to fix anything in the code myself, and I was able to prompt my way through all the errors and problems that occurred.&lt;/p&gt;

&lt;p&gt;That said, if I hadn't known anything about web development, it would have been a different experience. Q CLI got Tailwind CSS 3 and 4 mixed up and it would have caused an issue if I blindly accepted all the change. On the other hand, I’m confident I could have prompted my way to fix it, it would have just taken longer. This issue may be certainly fixed by the time you read this, but just realize when you are working with the latest libraries, your favorite coding assistant may not be up to date.&lt;/p&gt;

&lt;p&gt;Is vibe coding a fad? I don’t think so. This is a perfectly valid way of creating software, and as LLMs and AI coding assistants get better the number of corrections or errors will decrease. Vibe coding is certainly not a substitute for a real software developer, and I can imagine as you try to vibe code into a much more complicated app, it won’t work as well. You’ll always need to have a developer to help out where the LLM lacks. As for me, I’ll be looking to update my personal site with this new code soon! After I vibe code a few more updates. 😉&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tailwindcss</category>
      <category>genai</category>
      <category>aws</category>
    </item>
    <item>
      <title>How To Create A Fullstack TypeScript App Using AWS Amplify Gen 2</title>
      <dc:creator>Erik Hanchett</dc:creator>
      <pubDate>Fri, 31 May 2024 19:06:37 +0000</pubDate>
      <link>https://forem.com/aws/how-to-create-an-app-on-aws-aws-amplify-gen-2-2534</link>
      <guid>https://forem.com/aws/how-to-create-an-app-on-aws-aws-amplify-gen-2-2534</guid>
      <description>&lt;p&gt;&lt;a href="https://docs.amplify.aws/" rel="noopener noreferrer"&gt;AWS Amplify Gen 2 &lt;/a&gt;is a brand new way to create fullstack applications on AWS. In this tutorial, we’ll explore how to get started creating a fullstack TypeScript application with Next.js! We’ll add storage and then connect it to our Amplify connected UI component library to upload files.&lt;/p&gt;

&lt;p&gt;With  Gen 2 we focused our efforts on creating a great developer experience. We wanted to make sure that you could focus on your frontend code instead of worrying about setting up your backend infrastructure. In this new version we created a brand new TypeScript safe DX that supports fast local development, and is backed by AWS Cloud Development Kit (CDK).&lt;/p&gt;

&lt;p&gt;We recently released Gen 2 for general availability. Feel free to let us know what you think and leave a comment below.&lt;/p&gt;

&lt;p&gt;With that said, before you get started makes sure your familiar with TypeScript, VSCode and Git. You'll also need a GitHub account and make sure Git is installed locally on the command line. We'll be using Next.js, however you don't need to be an expert in React to try out this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are We Building Today?
&lt;/h2&gt;

&lt;p&gt;We will be creating an app that allows you to create todos with pictures. You’ll be using the &lt;a href="https://github.com/aws-samples/amplify-next-template" rel="noopener noreferrer"&gt;Amplify Next Starter&lt;/a&gt; template and have it deployed to the AWS Amplify console. We'll create and connect to AWS AppSync, our managed GraphQL service, and use &lt;a href="https://aws.amazon.com/pm/cognito" rel="noopener noreferrer"&gt;Amazon Cognito&lt;/a&gt;, our customer identity and access management service, to authenticate and authorize users. Additionally, we'll use S3 for file storage of our images.&lt;/p&gt;

&lt;p&gt;In this tutorial I'll walk you through step-by-step on how to create a new AWS Amplify Gen 2 app using the console, cloning that app, opening it in VSCode and adding in some additional features. We'll then deploy our changes back to the Amplify Gen 2 console to our hosted environment.&lt;/p&gt;

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

&lt;p&gt;We need to first have an AWS account ready. If you are not signed up, try out the &lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;AWS Free Tier&lt;/a&gt;! This will give you plenty of free resources to try out AWS and many of it's services. Keep in mind, all the services we'll work with today are on-demand and you'll only get charged when you are using them. &lt;/p&gt;

&lt;p&gt;We will be using the Next.js Amplify &lt;a href="https://github.com/new?template_name=amplify-next-template&amp;amp;template_owner=aws-samples&amp;amp;name=amplify-next-template&amp;amp;description=My%20Amplify%20Gen%202%20starter%20application" rel="noopener noreferrer"&gt;starter template&lt;/a&gt; as described in the &lt;a href="https://docs.amplify.aws/react/start/quickstart/" rel="noopener noreferrer"&gt;Quickstart guide&lt;/a&gt;. Clone the repository using the Amplify Next.js Template into your own Github account to get started. &lt;/p&gt;

&lt;p&gt;Afterwords, sign into the &lt;a href="https://signin.aws.amazon.com/signin" rel="noopener noreferrer"&gt;AWS management console&lt;/a&gt;. Search at the top for AWS Amplify, and click it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6wdqz81rm6okvxkckhu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6wdqz81rm6okvxkckhu.png" alt="AWS console choosing Amplify button" width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Amplify Console choose Create new app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq38cj20yqzoisaiyr6j9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq38cj20yqzoisaiyr6j9.png" alt="AWS console clicking create new app button" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select GitHub as the Git provider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbt3abc1jiwtvt5r745ya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbt3abc1jiwtvt5r745ya.png" alt="AWS console selecting the GitHub provider button" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next page you'll see a popup asking to connect to your Github account. This will give Amplify access to your account so it can connect to the Next.js repo you just created in the previous step from the starter template.&lt;/p&gt;

&lt;p&gt;Select your app from the App name field and click next. If you don’t see your app click the &lt;code&gt;Update Github permissions&lt;/code&gt; button and re-authorize with Github. On the next page leave all the default settings and click next. On the last page click &lt;code&gt;Save and deploy&lt;/code&gt;. Amplify will now begin to deploy your new app!&lt;/p&gt;

&lt;p&gt;The deployment may take several minutes. It will host your Next.js frontend and create infrastructure for your Amazon Cognito and AWS AppSync service backend. We’ll discuss this more later, and how we can make updates and changes to it. Until then, let's jump into VSCode and setup the frontend!&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating the frontend
&lt;/h2&gt;

&lt;p&gt;Let's take a look at our app, and make some updates.&lt;/p&gt;

&lt;p&gt;Open up your favorite terminal and clone the Github repo you just created in the previous section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone &amp;lt;The_Copied_Githhub_URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open up VSCode to that cloned project and open a terminal to the project’s root folder. Run the following 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 i @aws-amplify/ui-react-storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will install the &lt;a href="https://ui.docs.amplify.aws" rel="noopener noreferrer"&gt;Amplify UI component library&lt;/a&gt;. We'll use this later to add a file uploader, called Storage Manager, to our frontend to help with uploading pictures. &lt;/p&gt;

&lt;p&gt;If you look at the app folder structure you'll notice an amplify folder. This folder houses all the resources and files that will help us connect to our backend.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1ng7hn1yxa8f8f0f4tk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1ng7hn1yxa8f8f0f4tk.png" alt="List of folders for amplify" width="754" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By convention, the starter template will create an Amazon Cognito instance, and configure it for email login. The configuration for this will be in the resources file at &lt;code&gt;amplify/auth/resource.ts&lt;/code&gt;. For more information on the auth resource please checkout the &lt;a href="https://docs.amplify.aws/react/build-a-backend/auth/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It also creates an AWS Appsync instance with a &lt;code&gt;Todo&lt;/code&gt; schema in the &lt;code&gt;amplify/data/resource.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Update the &lt;code&gt;data/resource.ts&lt;/code&gt; to match the code below. The only difference is we are adding a new key to the model.&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="c1"&gt;// amplify/data/resource.ts&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;({&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;a&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&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="nf"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;allow&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;allow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publicApiKey&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ClientSchema&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;schema&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;export&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="nf"&gt;defineData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;authorizationModes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;defaultAuthorizationMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;apiKey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;apiKeyAuthorizationMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;expiresInDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This schema does a few things.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds a new Todo model with all the resolvers needed CRUDL (Create, Read, Update, Delete, List) and connects it to a new DynamoDB instance.&lt;/li&gt;
&lt;li&gt;The Todo model will have a content and a key  fields - both are strings.&lt;/li&gt;
&lt;li&gt;Adds public authorization rules so all users can create, read, update and delete, as long as they have the public api key.&lt;/li&gt;
&lt;li&gt;Sets a default authorization mode of apiKey and sets the expiration for 30 days. This is the public key.&lt;/li&gt;
&lt;li&gt;Exports a Schema that can be used by the frontend to ensure type safety end-to-end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This resource data pattern is very powerful by allowing the user to completely configure the backend data schema all in code using TypeScript.&lt;/p&gt;

&lt;p&gt;For the sake of this tutorial we will leave the default resource file unchanged for auth in the &lt;code&gt;amplify/auth/resources.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Storage
&lt;/h2&gt;

&lt;p&gt;We need to be able to upload images and retrieve them. We can do this using the Amplify Storage Category. &lt;/p&gt;

&lt;p&gt;Create a new folder at &lt;code&gt;amplify/storage&lt;/code&gt;. Inside this folder create a &lt;code&gt;resource.ts&lt;/code&gt; file. Copy the following code.&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="c1"&gt;// amplify/storage/resource.ts&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;defineStorage&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="s2"&gt;@aws-amplify/backend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineStorage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todosStorage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;access&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allow&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;media/*&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;allow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;guest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;read&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;write&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="c1"&gt;// additional actions such as "write" and "delete" can be specified depending on your use case&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;p&gt;This will create a public bucket for users to upload our images too. Users will be able to upload images to the &lt;code&gt;media&lt;/code&gt; folder and they’ll have access to &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, and &lt;code&gt;delete&lt;/code&gt; them. &lt;/p&gt;

&lt;p&gt;Next, let’s add this storage resource to our &lt;code&gt;backend.ts&lt;/code&gt; file. Open the &lt;code&gt;backend.ts&lt;/code&gt; file and add a new &lt;code&gt;storage&lt;/code&gt; import. The complete file is below.&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="c1"&gt;//amplify/backend.ts&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;defineBackend&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="s2"&gt;@aws-amplify/backend&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;auth&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="s2"&gt;./auth/resource.js&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;data&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="s2"&gt;./data/resource.js&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;storage&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="s2"&gt;./storage/resource.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;defineBackend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;auth&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="nx"&gt;storage&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;All our backend resource should be ready to go!&lt;/p&gt;

&lt;h3&gt;
  
  
  Starting the Sandbox
&lt;/h3&gt;

&lt;p&gt;To test locally we'll create a new ephemeral environment using Amplify tooling.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you've never setup AWS on the command line you'll need to run a few commands here to make sure your environment can connect to your account. Please follow the &lt;a href="https://docs.amplify.aws/gen2/start/account-setup/" rel="noopener noreferrer"&gt;Set up your AWS Account section&lt;/a&gt; section in our docs before continuing on.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ampx sandbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will create an environment in AWS based on the resources you configured in your &lt;code&gt;amplify&lt;/code&gt; folder. In this case it will create a Amazon Cognito, S3 Storage and AWS AppSync service. At any time you can stop this command, and it will delete all the resources it just created.&lt;/p&gt;

&lt;p&gt;It will also create an &lt;code&gt;amplify_outputs.json&lt;/code&gt; file in the root of your app. We'll need this file in the next section to setup our client &lt;code&gt;aws-amplify&lt;/code&gt; library so it can talk to the backend services.&lt;/p&gt;

&lt;p&gt;This will take a few minutes to run, after it completes continue onto the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update page.tsx
&lt;/h2&gt;

&lt;p&gt;The starter template has a built in todos app in it. Let’s modify the &lt;code&gt;page.tsx&lt;/code&gt; file so we can upload files and connect them with our todos.&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;page.tsx&lt;/code&gt; app add an import for &lt;code&gt;StorageManager&lt;/code&gt;, &lt;code&gt;StorageImage&lt;/code&gt; and to our Amplify UI components library.&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="c1"&gt;// app/page.tsx&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;StorageImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StorageManager&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="s2"&gt;@aws-amplify/ui-react-storage&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;Card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Flex&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;Button&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="s2"&gt;@aws-amplify/ui-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;p&gt;The UI components, &lt;code&gt;Card&lt;/code&gt;, &lt;code&gt;Flex&lt;/code&gt;, &lt;code&gt;Text&lt;/code&gt; and &lt;code&gt;Button&lt;/code&gt; are used to style our app. The &lt;code&gt;StorageImage&lt;/code&gt; and &lt;code&gt;StorageManager&lt;/code&gt; will allow us to add images and show them.&lt;/p&gt;

&lt;p&gt;We need to be able to delete todos. Add the following function under the &lt;code&gt;listTodos()&lt;/code&gt;.&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="c1"&gt;// app/page.tsx&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
 &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deleteTodo&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="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="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&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="k"&gt;delete&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll need to update the &lt;code&gt;createTodo&lt;/code&gt; function. It will now take in two arguments, &lt;code&gt;key&lt;/code&gt; and &lt;code&gt;content&lt;/code&gt;.&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="c1"&gt;// app/page.tsx&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createTodo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;key&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="nl"&gt;content&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="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&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="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;key&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;p&gt;We’ll now do a few updates to the return statement. We’ll add a new &lt;code&gt;StorageImage&lt;/code&gt; that will display our images. We’ll also add in the &lt;code&gt;StorageManager&lt;/code&gt;. This will display a component so we can upload images.  &lt;/p&gt;

&lt;p&gt;Update the return so it matches below.&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="c1"&gt;// app/page.tsx&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;main&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;h1&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;todos&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&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;ul&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="nf"&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="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;li&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;todo&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;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="nf"&gt;deleteTodo&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="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;Flex&lt;/span&gt; &lt;span class="nx"&gt;justifyContent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;space-between&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;Text&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;content&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;/Text&lt;/span&gt;&lt;span class="err"&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;key&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;StorageImage&lt;/span&gt;
                  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&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;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                  &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&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;content&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                  &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100px&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="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&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;/Flex&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;/li&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;/ul&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;StorageManager&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;media/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;acceptedFileTypes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
        &lt;span class="nx"&gt;maxFileCount&lt;/span&gt;&lt;span class="o"&gt;=&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;onUploadStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{({&lt;/span&gt; &lt;span class="nx"&gt;key&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;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Todos content&lt;/span&gt;&lt;span class="dl"&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;content&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="nf"&gt;createTodo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}}&lt;/span&gt;
        &lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
          &lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Card&lt;/span&gt; &lt;span class="nx"&gt;variation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;elevated&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;children&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;/Card&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="nc"&gt;FilePicker&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;onClick&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;variation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&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;onClick&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;Add&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;Choose&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="nx"&gt;For&lt;/span&gt; &lt;span class="nx"&gt;Upload&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&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="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;/main&lt;/span&gt;&lt;span class="err"&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;You may notice some props on the &lt;code&gt;StorageManager&lt;/code&gt;. The components prop can be used to completely override the look and feel of the component. In this case we changed the container slightly to add a card around it. We also changed the &lt;code&gt;FilePicker&lt;/code&gt; so we can use a different kind of button to upload.&lt;/p&gt;

&lt;p&gt;We added an event listener called &lt;code&gt;onUploadStart&lt;/code&gt;. This will trigger whenever an upload begins. It will first ask the user for the todos content. It will then call the &lt;code&gt;createTodo&lt;/code&gt; which will create the todo in the database.&lt;/p&gt;

&lt;p&gt;Here is the complete &lt;code&gt;app/page.tsx&lt;/code&gt; file.&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&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;useState&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Schema&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="s2"&gt;@/amplify/data/resource&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;generateClient&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="s2"&gt;aws-amplify/data&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;StorageImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StorageManager&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="s2"&gt;@aws-amplify/ui-react-storage&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;Card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Flex&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;Button&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="s2"&gt;@aws-amplify/ui-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;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;Amplify&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="s2"&gt;aws-amplify&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;outputs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/amplify_outputs.json&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="s2"&gt;@aws-amplify/ui-react/styles.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;Amplify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputs&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;generateClient&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Schema&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;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="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&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="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;listTodos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&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="nf"&gt;observeQuery&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&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="nx"&gt;items&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deleteTodo&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="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="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&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="k"&gt;delete&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="p"&gt;}&lt;/span&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="nf"&gt;listTodos&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createTodo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;key&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="nl"&gt;content&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="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&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="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;key&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&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;h1&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;todos&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&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;ul&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="nf"&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="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;li&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;todo&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;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="nf"&gt;deleteTodo&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="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;Flex&lt;/span&gt; &lt;span class="nx"&gt;justifyContent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;space-between&lt;/span&gt;&lt;span class="dl"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&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;content&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;/Text&lt;/span&gt;&lt;span class="err"&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;key&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;StorageImage&lt;/span&gt;
                  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&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;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                  &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&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;content&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                  &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100px&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="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&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;/Flex&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;/li&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;/ul&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;StorageManager&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;media/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;acceptedFileTypes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
        &lt;span class="nx"&gt;maxFileCount&lt;/span&gt;&lt;span class="o"&gt;=&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;onUploadStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{({&lt;/span&gt; &lt;span class="nx"&gt;key&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;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Todo content&lt;/span&gt;&lt;span class="dl"&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;content&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="nf"&gt;createTodo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}}&lt;/span&gt;
        &lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
          &lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Card&lt;/span&gt; &lt;span class="nx"&gt;variation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;elevated&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;children&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;/Card&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="nc"&gt;FilePicker&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;onClick&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;variation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&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;onClick&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;Add&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;Choose&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="nx"&gt;For&lt;/span&gt; &lt;span class="nx"&gt;Upload&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&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="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;/main&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;h2&gt;
  
  
  Trying it out
&lt;/h2&gt;

&lt;p&gt;Go ahead and start the app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The front page will load. Go ahead and click the &lt;code&gt;Add Todo&lt;/code&gt; and &lt;code&gt;Choose File for Upload button&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1ki6ngzgswqy4htej60.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1ki6ngzgswqy4htej60.png" alt="Storage Manager displayed with upload button" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose a todo name and then choose a file. You’ll see the upload occur and a new todo displayed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc22f3z7xoisymgdyo01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc22f3z7xoisymgdyo01.png" alt="Storage Manager with one amplify.png file uploaded" width="800" height="1007"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can go ahead and add a few more. You can also click any todo for them to delete.&lt;/p&gt;

&lt;p&gt;Good job! You've created a full stack app!&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy to Production
&lt;/h2&gt;

&lt;p&gt;In the first steps we created a new Next.js app using a starter template, we then cloned it down to edit it locally. We can now commit our changes and have it deployed back to our AWS Gen 2 console that will trigger a new branch build .&lt;/p&gt;

&lt;p&gt;If you like, you can go ahead and stop the sandbox environment. Just go to the terminal where it's running and hit Ctrl/Cmd C. If for some reason you accidentally closed the terminal, you can always stop any sandbox environments by going back to the Amplify Gen 2 console and choosing the &lt;code&gt;Manage Sandboxes&lt;/code&gt; button in the &lt;code&gt;All apps&lt;/code&gt; page.&lt;/p&gt;

&lt;p&gt;The sandbox environments are for testing only, and can be started or stopped at any time. The production environments are attached to your git branches that you pushed to Github. In this case the main branch is the production environment. If we choose to do so, we can create multiple environments if needed based on any branch we create and connect them to the AWS Gen 2 console.&lt;/p&gt;

&lt;p&gt;To deploy our changes with the new frontend updates, all we'll need to do is a git commit and push. Open the terminal in your project and paste the following commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Updated todos App"&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will push all our changes to the Amplify Gen 2 console and trigger another build. After a few minutes click on the domain listed in the Amplify console to see it in action!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial we learned how to get started with AWS Amplify Gen 2. We used the Amplify Next.js starter template to create a new Amplify hosted app with our backend. We then made a todo app with photo storage, used the sandbox environment to test, and then pushed it to production to see the changes!&lt;/p&gt;

&lt;p&gt;To clean up this environment, make sure to stop the sandbox environment and then go back into the amplify console, click on our app, then go to &lt;code&gt;App settings → General Settings&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffl58t1jeaz5wswod88ys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffl58t1jeaz5wswod88ys.png" alt="App settings drop down" width="564" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the &lt;code&gt;Delete app&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6tmm3zbepieery2qgix0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6tmm3zbepieery2qgix0.png" alt="Delete app button" width="276" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to learn more make sure to check our the &lt;a href="https://docs.amplify.aws" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>amplify</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Five Tutorials To Create Your Fullstack Apps Using AWS Amplify Gen 2</title>
      <dc:creator>Erik Hanchett</dc:creator>
      <pubDate>Fri, 01 Mar 2024 18:16:59 +0000</pubDate>
      <link>https://forem.com/aws/five-tutorials-to-create-your-fullstack-apps-using-aws-amplify-gen-2-29n3</link>
      <guid>https://forem.com/aws/five-tutorials-to-create-your-fullstack-apps-using-aws-amplify-gen-2-29n3</guid>
      <description>&lt;p&gt;&lt;a href="https://docs.amplify.aws/gen2/" rel="noopener noreferrer"&gt;AWS Amplify Gen 2&lt;/a&gt; is the next generation of tooling from Amplify! It’s a new code-first developer experience that helps frontend developers create fullstack applications on AWS. You can now author your frontend and backend definition completely in TypeScript, create ephemeral sandbox environments to test against, and deploy to production using Git branch-based environments.&lt;/p&gt;

&lt;p&gt;Since we’ve entered into developer preview at the end of last year a lot of content has been created! Let’s jump in!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2x2r4d9qrtv1tu5qpsnm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2x2r4d9qrtv1tu5qpsnm.png" alt="Tweet About Amplify Gen 2" width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://aws.amazon.com/blogs/mobile/introducing-amplify-gen2/" rel="noopener noreferrer"&gt;Introducing The Next Generation of AWS Amplify’s Fullstack Development Experience&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Amplify has unveiled a code-first way to develop fullstack applications. This post deep dives into why we decided to go this direction and how to get started. If you are looking for a overview of Gen 2, here is the place!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://dev.to/aws-builders/the-guide-to-set-up-nextjs-authentication-and-data-fetching-with-aws-amplify-gen2-2blf"&gt;The guide to set up NextJS Authentication and data fetching with AWS Amplify Gen2&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This post describes how to add auth and data to your application using Amplify Gen 2. This step-by-step tutorial covers creating a new Next.js 14 application, adding in Gen 2, and setting up authentication. It also shows how to use middleware to protect routes and use the generateClient API to add and list data from the backend.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnoyn4x4muw7k3tsu46j2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnoyn4x4muw7k3tsu46j2.png" alt="Tweet about Amplify Gen 2" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.youtube.com/watch?si=I70cAcds7lO0ZJTr&amp;amp;v=wcSMnICY-_8&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;How To Create A Full Stack Typesafe App With No Knowledge! Using AWS Gen2 and Next.js 14!&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This video describes how to create a fullstack application using Gen 2 with Next.js 14. In it, Erik creates a protected Next.js 14 application using auth. The application helps you store titles and allows multiple users to leave comments. Viewers will learn how to create, update, list, and delete comments and titles, as well as how to setup dynamic routes. All of this backed by Gen 2, and AWS AppSync.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://aws.amazon.com/blogs/mobile/the-future-of-web-development-aws-amplifys-code-first-approach/" rel="noopener noreferrer"&gt;The future of web development: AWS Amplify’s Code First Approach&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This extended article describes what Amplify Gen 2 is, the problems frontend developers face with fullstack applications, and how to resolve them. Christian uses several code examples to illustrate how to use Gen 2, extend its functionality and how to use it in your next project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomvztkmqgjqsvq5od1us.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomvztkmqgjqsvq5od1us.png" alt="Tweet about Amplify Gen 2" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.youtube.com/watch?si=JhoSMVEhrQuOU58l&amp;amp;v=MOeL0cKy7-Q&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;How To Add A Social Login To Your Website Using AWS!&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This advanced tutorial describes how to extend Amplify Gen 2’s functionality to add a Github social login. It deep dives into OAuth, OIDC, authorization and authentication.  It describes how AWS Amplify Cognito, as well as identity providers can work together with Gen 2 to create social logins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There you have it! Five different tutorials on how to use AWS Amplify Gen 2! Please leave a comment below if you’ve tried Gen 2! Thanks&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>aws</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Secrets of Styling Forms (Using AWS Amplify Studio)</title>
      <dc:creator>Erik Hanchett</dc:creator>
      <pubDate>Mon, 17 Jul 2023 21:37:19 +0000</pubDate>
      <link>https://forem.com/aws/secrets-of-styling-forms-using-aws-amplify-studio-49f0</link>
      <guid>https://forem.com/aws/secrets-of-styling-forms-using-aws-amplify-studio-49f0</guid>
      <description>&lt;p&gt;Forms are deceptively difficult. When you create a form for your website you need to make a lot of decisions. You have to worry about validation, structure, and how to send the data to your backend. You need to figure out how to handle errors, and how to style and configure your form. You’ll need to make sure your forms are accessible. With this in mind, &lt;a href="https://docs.amplify.aws/console/formbuilder/overview/" rel="noopener noreferrer"&gt;AWS Amplify Studio&lt;/a&gt; with its form builder can help!&lt;/p&gt;

&lt;p&gt;In this post I’ll discuss how to add a new form using Amplify Studio that is connected to an &lt;a href="https://docs.amplify.aws/console/data/data-model/" rel="noopener noreferrer"&gt;AWS Appsync data source&lt;/a&gt;. We’ll look at how to customize that form by adding in a dark/light mode. We’ll look at how we can further customize the validation rules and edit the error messages! We’ll then test our submissions by displaying the results using Amplify’s JavaScript GraphQL API library.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you'd like to see the completed code. Please check out this &lt;a href="https://github.com/ErikCH/StyleFormsExampleAmplify" rel="noopener noreferrer"&gt;repo&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;We’ll create a basic Todo form using Amplify studio. &lt;/p&gt;

&lt;p&gt;To begin, sign into your &lt;a href="https://aws.amazon.com/console/" rel="noopener noreferrer"&gt;AWS Console&lt;/a&gt; and search for AWS Amplify and click on it. If this is the first time using Amplify, choose &lt;code&gt;Get Started&lt;/code&gt; and click on the &lt;code&gt;Get started&lt;/code&gt; again under Amplify Studio. If you’ve already created an Amplify app in this region you can click &lt;code&gt;New app&lt;/code&gt; → &lt;code&gt;Build an app&lt;/code&gt;. Choose an app name, click &lt;code&gt;Confirm deployment&lt;/code&gt;. Afterwords click &lt;code&gt;Launch Studio&lt;/code&gt; to begin!&lt;/p&gt;

&lt;p&gt;If you are an existing Amplify Studio user, Launch Studio from your existing application. This feature is available to all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3a3iibkrd5932dvqcuts.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3a3iibkrd5932dvqcuts.png" alt="Launch Studio screenshot" width="500" height="694"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Data
&lt;/h2&gt;

&lt;p&gt;Inside Amplify Studio let’s create a data source that we can use with our form. Click on &lt;code&gt;Data&lt;/code&gt; in the menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6b9xtvbyb1ggrihlbnop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6b9xtvbyb1ggrihlbnop.png" alt="Data Tab" width="322" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Add model&lt;/code&gt; and create Todo. Add the completed and the title fields.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8v6ccnyi3dfxczp1qp8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8v6ccnyi3dfxczp1qp8.png" alt="Data modeling picture" width="800" height="857"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After adding these models make sure you click the &lt;code&gt;Save and Deploy&lt;/code&gt; button in the top right hand corner. It may take a few minutes to deploy.&lt;/p&gt;

&lt;p&gt;After the model is deployed you’ll see a page with some instructions to pull the latest client config into your React application. Make sure to copy the pull command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gs8mb24qbyb2rm5b4n9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gs8mb24qbyb2rm5b4n9.png" alt="Pull config instructions" width="800" height="893"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js App Setup
&lt;/h2&gt;

&lt;p&gt;Now that we have our data and forms in place we can pull the information into our existing application. I’ll assume that you’ve already created a new Next.js application. If not, and you are starting a new Next.js application, make sure the &lt;a href="https://ui.docs.amplify.aws/react/getting-started/usage/nextjs" rel="noopener noreferrer"&gt;App router turned off&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Open up your app and install these libraries with your favorite package manager.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @aws-amplify/ui-react aws-amplify react-icons
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If not already installed, make sure you also install the Amplify CLI globally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @aws-amplify/cli &lt;span class="nt"&gt;-g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this is the first time using Amplify in a while, make sure you upgrade to the latest version of the CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;amplify upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, take the command you copied from the last section and paste it into your terminal running it in the root of your Next.js project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;amplify pull &lt;span class="nt"&gt;--appId&lt;/span&gt; &amp;lt;replace-this-with-your-id&amp;gt; &lt;span class="nt"&gt;--envName&lt;/span&gt; &amp;lt;replace-with-env-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now see a few new files and directories in your project. &lt;/p&gt;

&lt;p&gt;Since we are dealing with AppSync, you’ll need to generate some GraphQL query files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;amplify codegen add
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate some helpful GraphQL query/subscription/mutation files that we’ll use later on in this tutorial.&lt;/p&gt;

&lt;p&gt;Let’s setup Amplify so we can use our new forms!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: If you are using Next.js with the app router or using Vite, you may need some small additional configuration. Please follow the &lt;a href="https://ui.docs.amplify.aws/react/getting-started/usage/nextjs" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; or &lt;a href="https://ui.docs.amplify.aws/react/getting-started/usage/vite" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; usage guide.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Our Form and Customizing it
&lt;/h2&gt;

&lt;p&gt;Let’s begin setting up our Next application so it can talk to our AppSync backend.&lt;/p&gt;

&lt;p&gt;Inside your main entry point file in your application, add these lines to configure Amplify with the correct styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;Amplify&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="s2"&gt;aws-amplify&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;awsExports&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../aws-exports&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;Amplify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;awsExports&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="s2"&gt;@aws-amplify/ui-react/styles.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;Next, we’ll be adding in a special theme file. This file is used to customize global themes throughout your application. In our case we will us to to override the default theme of our forms to support a &lt;code&gt;dark&lt;/code&gt; mode. The &lt;code&gt;defaultDarkModeOverride&lt;/code&gt; will add this for us.&lt;/p&gt;

&lt;p&gt;Create a new file called &lt;code&gt;theme.tsx&lt;/code&gt; in your &lt;code&gt;src&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;Theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultDarkModeOverride&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="s2"&gt;@aws-amplify/ui-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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;overrides&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;defaultDarkModeOverride&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’ll add this Theme provider to a new Layout file. In the &lt;code&gt;src/components&lt;/code&gt; folder create a new &lt;code&gt;Layout.tsx&lt;/code&gt; file. Inside this file we’ll add a new &lt;code&gt;colorMode&lt;/code&gt; useState variable that we’ll use to change the color of the theme. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ThemeProvider&lt;/code&gt; surrounds our application and provides theming capabilities with design tokens to our app. These tokens can completely override the look and feel of our application. The official &lt;a href="https://ui.docs.amplify.aws/react/theming/theme-provider" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; discuss this in more detail.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;theme&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="s2"&gt;@/theme&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;ColorMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ThemeProvider&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="s2"&gt;@aws-amplify/ui-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;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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;PropsWithChildren&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;colorMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setColorMode&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ColorMode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&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;ThemeProvider&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;colorMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;colorMode&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="nx"&gt;children&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;ThemeProvider&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;Let’s add in a toggle button so users can select between dark and light mode. We’ll be using the react-icons/md  set for these icons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;theme&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="s2"&gt;@/theme&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;MdOutlineDarkMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MdOutlineLightMode&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="s2"&gt;react-icons/md&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;ColorMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Flex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ToggleButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ToggleButtonGroup&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="s2"&gt;@aws-amplify/ui-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;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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;PropsWithChildren&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;colorMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setColorMode&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ColorMode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&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;ThemeProvider&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;colorMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;colorMode&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;Flex&lt;/span&gt; &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"column"&lt;/span&gt; &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100vw"&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100vh"&lt;/span&gt; &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"background.primary"&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;ToggleButtonGroup&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;colorMode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"small"&lt;/span&gt;
            &lt;span class="na"&gt;isExclusive&lt;/span&gt;
            &lt;span class="na"&gt;onChange&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;value&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;setColorMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ColorMode&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="nc"&gt;ToggleButton&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"light"&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;MdOutlineLightMode&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;ToggleButton&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;ToggleButton&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"dark"&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;MdOutlineDarkMode&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;ToggleButton&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;ToggleButtonGroup&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;children&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;Flex&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;ThemeProvider&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;The &lt;code&gt;ToggleButtonGroup&lt;/code&gt; is from a set of UI primitives offered by the &lt;code&gt;@aws-amplify/ui-react&lt;/code&gt; library. It creates a nice looking toggle button. On every click of the toggle button the &lt;code&gt;colorMode&lt;/code&gt; is set, and is updated in the &lt;code&gt;ThemeProvider&lt;/code&gt;. This will cause the screen to change between dark and light mode.&lt;/p&gt;

&lt;p&gt;Finally, we’ll add in an &lt;code&gt;AppHeader&lt;/code&gt; component to help sticky this layout to the top left hand corner. We’ll also add a nice icon using the &lt;code&gt;Icon&lt;/code&gt; component primitive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;theme&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="s2"&gt;@/theme&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;MdOutlineDarkMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MdOutlineLightMode&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="s2"&gt;react-icons/md&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;ColorMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Flex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ToggleButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ToggleButtonGroup&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;@aws-amplify/ui-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;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AppHeader&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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;PropsWithChildren&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;Flex&lt;/span&gt;
      &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"header"&lt;/span&gt;
      &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;
      &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"space-between"&lt;/span&gt;
      &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"center"&lt;/span&gt;
      &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1rem"&lt;/span&gt;
      &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"small"&lt;/span&gt;
      &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"sticky"&lt;/span&gt;
      &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
      &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt;
      &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"background.primary"&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;children&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;Flex&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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;PropsWithChildren&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;colorMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setColorMode&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ColorMode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&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;ThemeProvider&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;colorMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;colorMode&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;Flex&lt;/span&gt;
        &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"column"&lt;/span&gt;
        &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100vw"&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100vh"&lt;/span&gt;
        &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"background.primary"&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;AppHeader&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;Icon&lt;/span&gt;
            &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"brand.primary.60"&lt;/span&gt;
            &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"xl"&lt;/span&gt;
            &lt;span class="na"&gt;paths&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;d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;M10.8484 4.19838C10.7939 4.2926 10.7939 4.40867 10.8484 4.50288L21.3585 22.6711C21.413 22.7653 21.5138 22.8233 21.6228 22.8233H23.9901C24.225 22.8233 24.3718 22.5696 24.2543 22.3666L12.5605 2.15225C12.4431 1.94925 12.1495 1.94925 12.0321 2.15225L10.8484 4.19838Z&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="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;M15.2084 22.6711C15.2629 22.7653 15.3636 22.8233 15.4726 22.8233H17.8461C18.081 22.8233 18.2278 22.5696 18.1104 22.3666L9.48857 7.46259C9.37113 7.25959 9.07755 7.25959 8.96011 7.46259C6.09213 12.4203 3.21732 17.4003 0.336955 22.3816C0.219574 22.5846 0.366371 22.8383 0.601212 22.8383H11.7185C11.9533 22.8383 12.1001 22.5846 11.9827 22.3816L10.8455 20.4158C10.791 20.3216 10.6903 20.2635 10.5813 20.2635H4.8952C4.77776 20.2635 4.70437 20.1367 4.76308 20.0352L9.0912 12.5534C9.14991 12.4519 9.29671 12.4519 9.35542 12.5534L15.2084 22.6711Z&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="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="nc"&gt;ToggleButtonGroup&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;colorMode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"small"&lt;/span&gt;
            &lt;span class="na"&gt;isExclusive&lt;/span&gt;
            &lt;span class="na"&gt;onChange&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;value&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;setColorMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ColorMode&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="nc"&gt;ToggleButton&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"light"&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;MdOutlineLightMode&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;ToggleButton&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;ToggleButton&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"dark"&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;MdOutlineDarkMode&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;ToggleButton&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;ToggleButtonGroup&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;AppHeader&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;children&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;Flex&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;ThemeProvider&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;We’ll need to update the &lt;code&gt;_app.tsx&lt;/code&gt; file so our new layout surrounds the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageProps&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;AppProps&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;Layout&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;Component&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;pageProps&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;Layout&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;Toggle the dark mode and see it in action.&lt;/p&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzqb6a2lgk8z9clzbav0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzqb6a2lgk8z9clzbav0.png" alt="Header with Light Mode" width="800" height="96"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1trbf909ldzsfglul9vb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1trbf909ldzsfglul9vb.png" alt="Header with dark mode" width="800" height="78"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding our form and customizing them
&lt;/h2&gt;

&lt;p&gt;Let’s now add in our form to the main route &lt;code&gt;index.tsx&lt;/code&gt; file in our Next.js application.&lt;/p&gt;

&lt;p&gt;Import the form onto the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;TodoForm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/ui-components/TodoCreateForm&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodoForm&lt;/span&gt;&lt;span class="p"&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;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xbvfe9we4w6kcbe8teh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xbvfe9we4w6kcbe8teh.png" alt="Todo form with title clear and submit" width="556" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By convention this form is already connected to the AppSync model we created earlier. Under the covers it uses something called &lt;a href="https://docs.amplify.aws/lib/datastore/getting-started/q/platform/js/" rel="noopener noreferrer"&gt;DataStore&lt;/a&gt; to sync and send data to the backend. &lt;/p&gt;

&lt;p&gt;We can customize this form to our liking. Let’s update the validation rule first. Assume that we don’t want users to enter in empty spaces. We’ll add a check for that and display a custom error message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;TodoForm&lt;/span&gt;
    &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
    &lt;span class="na"&gt;onValidate&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;validateResponse&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;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="na"&gt;hasError&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="na"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please enter a value!&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="p"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;validateResponse&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this validation in place, users won’t be able to submit a blank input in without getting an error. The forms logic will handle the rest and will disable the input as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fekdyk7gicy13kyvafj3w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fekdyk7gicy13kyvafj3w.png" alt="Todo form showing error- please enter value" width="494" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine we want to show a message every time the user successfully submit a todo. The generated forms have event handlers that you can hook into and listen for. In this example, we’ll create a &lt;code&gt;showSuccess&lt;/code&gt; variable that we can use to trigger an alert message, that fades after two seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;showSuccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setShowSuccess&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="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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TodoForm&lt;/span&gt;
          &lt;span class="nx"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;onValidate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;validateResponse&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;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="na"&gt;hasError&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="na"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please enter a value!&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="p"&gt;}&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;validateResponse&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;onSuccess&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="nf"&gt;setShowSuccess&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="nf"&gt;setTimeout&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;setShowSuccess&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="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;showSuccess&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="nc"&gt;Alert&lt;/span&gt; &lt;span class="na"&gt;variation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Todo added!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Alert&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;The &lt;code&gt;onSuccess&lt;/code&gt; handler will trigger as soon as the form has submitted successfully. We can then show an alert! This &lt;code&gt;Alert&lt;/code&gt; is another &lt;code&gt;@aws-amplify/ui-react&lt;/code&gt; UI primitive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzrgl9fe12vpfhjzyzdg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzrgl9fe12vpfhjzyzdg.png" alt="Todo form showing alert message Todo added" width="500" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a way to add todo’s, we will need a way to show them.&lt;/p&gt;

&lt;p&gt;Let’s add a method that grabs all the todos. This will use the Amplify JS library GraphQL API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;API&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;graphqlOperation&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="s2"&gt;aws-amplify&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;GraphQLQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GraphQLSubscription&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="s2"&gt;@aws-amplify/api&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;ListTodosQuery&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;OnCreateTodoSubscription&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="s2"&gt;@/API&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;listTodos&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="s2"&gt;@/graphql/queries&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;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTodo&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getTodos&lt;/span&gt; &lt;span class="o"&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="nx"&gt;allTodos&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;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GraphQLQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ListTodosQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;listTodos&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;filteredTodos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allTodos&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="nx"&gt;listTodos&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&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="o"&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;_deleted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;setTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filteredTodos&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Todo&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;Since we created our application using Studio and form builder, by convention our forms uses &lt;a href="https://docs.amplify.aws/lib/datastore/getting-started/q/platform/js/" rel="noopener noreferrer"&gt;DataStore&lt;/a&gt; and &lt;code&gt;conflict resolution&lt;/code&gt; turned on in the background. Conflict resolution creates a versioned data source that enhances the object data model with metadata. At this time, you cannot turn this off when using Form Builder and studio. &lt;/p&gt;

&lt;p&gt;What that means is that our Data model has a few extra meta fields including  &lt;code&gt;_deleted&lt;/code&gt; and  &lt;code&gt;_version&lt;/code&gt; . These are automatically added to our data model when the model was created. When you delete a record, it doesn’t get deleted, the way you normally may expect. Instead, the &lt;code&gt;_deleted&lt;/code&gt; field gets set to &lt;code&gt;true&lt;/code&gt; and the record remains. Keep this in mind when dealing with data with &lt;code&gt;conflict resolution&lt;/code&gt; turned on. This is why we must filter out records with &lt;code&gt;_deleted&lt;/code&gt; set to true in our code, that way we don’t accidentally show deleted data.&lt;/p&gt;

&lt;p&gt;It’s good to know that if you decide to make updates or deletions to any record using the Amplify GraphQL API, you must also include the &lt;code&gt;_version&lt;/code&gt; in the GraphQL input since conflict resolution is turned on. The &lt;code&gt;_version&lt;/code&gt; field will increment on every change that occurs to the record. You’ll need to keep track of this &lt;code&gt;_version&lt;/code&gt; field when you make updates or delete an item.&lt;/p&gt;

&lt;p&gt;To guarantee the correct order, we sort by the &lt;code&gt;createdAt&lt;/code&gt; time.&lt;/p&gt;

&lt;p&gt;Now that we have a way to grab data, we’ll create a GraphQL subscription that will load as soon as the application loads using a &lt;code&gt;useEffect&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;API&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;graphqlOperation&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="s2"&gt;aws-amplify&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;GraphQLQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GraphQLSubscription&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="s2"&gt;@aws-amplify/api&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;ListTodosQuery&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;OnCreateTodoSubscription&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="s2"&gt;@/API&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;onCreateTodo&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="s2"&gt;@/graphql/subscriptions&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;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&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="s2"&gt;react&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;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTodo&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="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="nf"&gt;getTodos&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;sub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GraphQLSubscription&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;OnCreateTodoSubscription&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;graphqlOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onCreateTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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;setTodo&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prevValue&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="nx"&gt;prevValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;value&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="nx"&gt;onCreateTodo&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Todo&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;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unsubscribe&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;p&gt;We first trigger the &lt;code&gt;getTodos&lt;/code&gt; and then run our subscription. As soon as a new Todo is created, the subscription is triggered. We can then use our &lt;code&gt;setTodo&lt;/code&gt; to save the data into the array. Alternatively, we could call &lt;code&gt;getTodos&lt;/code&gt; again on every subscription change, however for the sake of this post we’ll manipulate the &lt;code&gt;todos&lt;/code&gt; array and add the new todo to it instead. &lt;/p&gt;

&lt;p&gt;Let’s put this all together into one file. I’ve added a few more UI primitives to make things easier including a &lt;code&gt;Collection&lt;/code&gt; component that is used with the array to display data in a nice row.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;Alert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Flex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Heading&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;useTheme&lt;/span&gt;&lt;span class="p"&gt;,&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="s2"&gt;@aws-amplify/ui-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;TodoForm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/ui-components/TodoCreateForm&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;API&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;graphqlOperation&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="s2"&gt;aws-amplify&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;GraphQLQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GraphQLSubscription&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="s2"&gt;@aws-amplify/api&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;ListTodosQuery&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;OnCreateTodoSubscription&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="s2"&gt;@/API&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;listTodos&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="s2"&gt;@/graphql/queries&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;onCreateTodo&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="s2"&gt;@/graphql/subscriptions&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;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&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="s2"&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&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;showSuccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setShowSuccess&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="kc"&gt;false&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;setTodo&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getTodos&lt;/span&gt; &lt;span class="o"&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="nx"&gt;allTodos&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;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GraphQLQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ListTodosQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;listTodos&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;filteredTodos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allTodos&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="nx"&gt;listTodos&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&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="o"&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;_deleted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;setTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filteredTodos&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;[]);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&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="nf"&gt;getTodos&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;sub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GraphQLSubscription&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;OnCreateTodoSubscription&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;graphqlOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onCreateTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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;setTodo&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prevValue&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="nx"&gt;prevValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;value&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="nx"&gt;onCreateTodo&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Todo&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;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unsubscribe&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTheme&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;Flex&lt;/span&gt;
      &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt;
      &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"stretch"&lt;/span&gt;
      &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0"&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;Flex&lt;/span&gt;
        &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"column"&lt;/span&gt;
        &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"medium"&lt;/span&gt;
        &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"xxl"&lt;/span&gt;
        &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"background.primary"&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;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;New ToDo&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&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;TodoForm&lt;/span&gt;
          &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
          &lt;span class="na"&gt;onValidate&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;validateResponse&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;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="na"&gt;hasError&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="na"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please enter a value!&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="p"&gt;}&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;validateResponse&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;onSuccess&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;setShowSuccess&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="nf"&gt;setTimeout&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;setShowSuccess&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="mi"&gt;2000&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="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;showSuccess&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="nc"&gt;Alert&lt;/span&gt; &lt;span class="na"&gt;variation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Todo added!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Alert&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="nc"&gt;Flex&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;Flex&lt;/span&gt;
        &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"column"&lt;/span&gt;
        &lt;span class="na"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;
        &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"xxl"&lt;/span&gt;
        &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"background.secondary"&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;Heading&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;List of Todos&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Heading&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;Collection&lt;/span&gt; &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"small"&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;"list"&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&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;(&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;
              &lt;span class="na"&gt;variation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"elevated"&lt;/span&gt;
              &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&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="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;marginTop&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1rem"&lt;/span&gt;
              &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1rem"&lt;/span&gt;
              &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;textDecoration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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;completed&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;line-through&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&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="si"&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;title&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;Card&lt;/span&gt;&lt;span class="p"&gt;&amp;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;Collection&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;Flex&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;Flex&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;Let’s take a look at it in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpu340tlxotfr7bb2z28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpu340tlxotfr7bb2z28.png" alt="Full todo form with list of todos" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And with dark mode, after clicking the dark mode icon!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjcsa0sa89lzgdu3f382.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjcsa0sa89lzgdu3f382.png" alt="Full todo form with list of todos in dark mode" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a lot more we could do here. We could add a way to take already existing todos and delete them and mark them complete. I’ll leave that exercise for you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Deletion
&lt;/h2&gt;

&lt;p&gt;Once you’re done with your application you can delete all your resources by running amplify delete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog post we’ve learned about Amplify Studio forms, how to customize them and how to add additional validation error messages. If you’d like to learn more about Amplify in general please check out the &lt;a href="https://docs.amplify.aws/" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;. You can also check the &lt;a href="https://docs.amplify.aws/console/tutorial/buildui/" rel="noopener noreferrer"&gt;Amplify Studio tutorial&lt;/a&gt; for more information on how to use it.&lt;/p&gt;

&lt;p&gt;Please let me know if you have any questions, I’m at &lt;a href="https://twitter.com/erikch" rel="noopener noreferrer"&gt;ErikCH on Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>aws</category>
      <category>amplify</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Front End Flash from AWS Amplify</title>
      <dc:creator>Erik Hanchett</dc:creator>
      <pubDate>Wed, 24 May 2023 15:31:28 +0000</pubDate>
      <link>https://forem.com/aws/front-end-flash-from-aws-amplify-4jah</link>
      <guid>https://forem.com/aws/front-end-flash-from-aws-amplify-4jah</guid>
      <description>&lt;p&gt;Hello and welcome to the &lt;a href="https://aws.amazon.com/amplify/" rel="noopener noreferrer"&gt;AWS Amplify&lt;/a&gt; community roundup post!&lt;/p&gt;

&lt;p&gt;Amplify is a set of tools for frontend and mobile developers to build, ship, and host full-stack applications on AWS.&lt;/p&gt;

&lt;p&gt;This post is meant to be a place where you can find the latest information on Amplify, AppSync - our fully managed GraphQL service, and all things frontend and mobile!  We’ll be showcasing our latest tutorials, guides, conference talks and some future plans we have in place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a Post?
&lt;/h2&gt;

&lt;p&gt;Many internal and external teams at Amplify create great content. From our developer advocate team to marketing, docs, solution architects and our community members. This content has an amazing amount of insight that I'd like to share with all of you!&lt;/p&gt;

&lt;p&gt;This post will help highlight that content, and hopefully help educate you on what we offer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback
&lt;/h2&gt;

&lt;p&gt;If you have a minute please complete this &lt;a href="https://pulse.buildon.aws/survey/PMYYIAUU" rel="noopener noreferrer"&gt;short survey&lt;/a&gt;. This will really help us make this post better!&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating SaaS Applications With AppSync and Amplify!
&lt;/h2&gt;

&lt;p&gt;Have you ever imagined creating a software as a service (Saas) startup? Did you know Amplify has all the tools you need to get started? Check out this tweet from Ryan!&lt;/p&gt;

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

&lt;p&gt;It’s really nice to see real people using AWS Amplify to solve their problems. I really liked how Ryan used Amplify and the AWS Free Tier with AWS Startups to get thousands of free credits! &lt;/p&gt;

&lt;p&gt;Michael Liendo talked about this very subject recently during the AWS Developer Innovation Day!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/videos/1804180223" rel="noopener noreferrer"&gt;Build a full-stack, fully typed app with GraphQL and AWS AppSync (fast forward to 01:26:50)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure you watch some of the other talks too, you’ll get a good idea of many of the innovations that AWS provides.&lt;/p&gt;

&lt;p&gt;He also open source his contributions so you can take a look at his code on Github!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/focusOtter/microsaas-backend" rel="noopener noreferrer"&gt;MicroSaas Backend&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Modern Apps With Amplify
&lt;/h2&gt;

&lt;p&gt;The non-stop innovations in the web space amazes me. Every month there is a new library or framework that I must try out! Here at AWS we want to meet developers where their at. We want you to build on modern tooling and frameworks and not worry about the infrastructure. Here are some tutorials and guides that follow these principles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Astro, SolidJS and Data With Amplify
&lt;/h3&gt;

&lt;p&gt;Have you tried out Astro or SolidJS? Christian Nwamba recently published two excellent tutorials on how to deploy a SolidJS and Astro Blog to Amplify hosting!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/BzF-IlzWn0o" rel="noopener noreferrer"&gt;Deploy a SolidJS App With AWS Amplify&lt;br&gt;
&lt;/a&gt;&lt;br&gt;
and&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/EPsF6SM5Nlw" rel="noopener noreferrer"&gt;Deploy an Astro Blog With AWS Amplify&lt;br&gt;
&lt;/a&gt;&lt;br&gt;
Christian also created an excellent video on how to work with data APIs, and how to get the correct types from the backend to the frontend using AWS AppSync.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/Mg8Mqb0AFlQ" rel="noopener noreferrer"&gt;Better Developer Experience When Working With Data APIs&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Sign In With Google With Amplify Authenticator
&lt;/h3&gt;

&lt;p&gt;A very common use case that we often see in modern web development is logging in with Google. This allows customers with a Google account to login with a few clicks.&lt;/p&gt;

&lt;p&gt;Amplify supports this feature, and in the following shorts Erik Hanchett shows how you can add this to your website in minutes!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/shorts/21PL0d-XlAM" rel="noopener noreferrer"&gt;Add A Sign In With Google To Your Website Part 1&lt;br&gt;
&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/shorts/iTP32OIo1Ak" rel="noopener noreferrer"&gt;Add A Sign In With Google To Your Website Part 2&lt;br&gt;
&lt;/a&gt;&lt;br&gt;
Erik has also been busy live streaming building a full-stack app! He’s been using a modern stack including, Amplify services, Next.js, AppSync, lambdas and more! You can find him live streaming weekly on his YouTube channel on Fridays, or you can watch his last two videos on demand!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=0gfg8zM5OWk" rel="noopener noreferrer"&gt;Building a full-stack app with Authenticator Live streamed April 6&lt;br&gt;
&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/live/NXCSZyPczvw?feature=share" rel="noopener noreferrer"&gt;Building a full-stack app with Authenticator Live streamed April 14&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Flutter and Cross Platform Development
&lt;/h3&gt;

&lt;p&gt;Did you know we have a &lt;a href="https://docs.amplify.aws/lib/q/platform/flutter/" rel="noopener noreferrer"&gt;Flutter library&lt;/a&gt; that works directly with Amplify services? And we just recently updated our docs site with instructions on how to create a &lt;a href="https://docs.amplify.aws/start/q/integration/flutter/" rel="noopener noreferrer"&gt;budget tracking app&lt;/a&gt;? &lt;/p&gt;

&lt;p&gt;In this video Muhammed Salih Güler talks about cross platform development, how how the Amplify Flutter library works! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=eiJFPSNoH_U" rel="noopener noreferrer"&gt;CB Community Flutter&lt;br&gt;
&lt;/a&gt;&lt;br&gt;
Salih has also been busy live streaming! Check out his last live stream on using the Flame Engine with Flutter!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=P-kd44htOyY" rel="noopener noreferrer"&gt;Flame Engine Live Stream Livestream April 6&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Storage Manager and Liveness
&lt;/h3&gt;

&lt;p&gt;In April we launched our new &lt;a href="https://ui.docs.amplify.aws/react/connected-components/storage/storagemanager" rel="noopener noreferrer"&gt;Storage Manger UI&lt;/a&gt; and &lt;a href="https://ui.docs.amplify.aws/react/connected-components/liveness" rel="noopener noreferrer"&gt;Face Liveness&lt;/a&gt; components!&lt;/p&gt;

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

&lt;p&gt;The Liveness component helps deter fraud by using our Amazon Rekognition service. You may want to use a service like this for an ID verification system or for fraud detection. This is built into our React, Flutter, Android and Swift libraries.&lt;/p&gt;

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

&lt;p&gt;The Amplify UI StorageManger has been released for general availability! This file uploader makes it straight forward to upload files directly to S3, with only a few lines of boilerplate code! &lt;/p&gt;

&lt;h2&gt;
  
  
  Where in the World is Amplify ✈️ ?
&lt;/h2&gt;

&lt;p&gt;The developer advocate team has been busy traveling!  &lt;/p&gt;

&lt;p&gt;Muhammed Salih Güler will be giving a talk on June 2 at Flutter Connection in Paris! Stop on by and find Salih so you can learn about Amplify and Flutter! He'll also be at the &lt;a href="https://aws.amazon.com/events/summits/amsterdam/" rel="noopener noreferrer"&gt;AWS Summit in Amsterdam&lt;/a&gt; on June 1st and Flutter Berlin on June 7th.&lt;/p&gt;

&lt;p&gt;From May 31 - June 2, Michael Liendo and Erik Hanchett are giving talks at &lt;a href="https://www.renderatl.com/" rel="noopener noreferrer"&gt;RenderATL&lt;/a&gt; in Atlanta! Amplify will be sponsoring the conference so please look for our AWS booth and say hi if you’re at the conference&lt;/p&gt;

&lt;p&gt;Erik will also be giving a talk on May 24 - 26 - at &lt;a href="https://us.vuejs.org/" rel="noopener noreferrer"&gt;VueConf US&lt;/a&gt; 2023 in New Orleans! Erik will have some stickers on him, so feel free to stop him and grab some.&lt;/p&gt;

&lt;p&gt;If you’re a part of the &lt;a href="https://aws.amazon.com/developer/community/community-builders/" rel="noopener noreferrer"&gt;community builders&lt;/a&gt; program with AWS, you can also here Michael Liendo give a talk on MicroSaaS architecture on May 24th! We are always looking for more community developers so feel free to signup and you might be chosen!&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;As you can see we’ve had a busy few months! What content would you like to see? Leave a comment or tweet me at &lt;a href="https://twitter.com/ErikCH" rel="noopener noreferrer"&gt;@ErikCH&lt;/a&gt;! Thanks&lt;/p&gt;

</description>
      <category>aws</category>
      <category>amplify</category>
      <category>webdev</category>
    </item>
    <item>
      <title>5 New Features For Amplify UI and Studio You Should Try Today</title>
      <dc:creator>Erik Hanchett</dc:creator>
      <pubDate>Wed, 22 Feb 2023 20:19:23 +0000</pubDate>
      <link>https://forem.com/erikch/5-new-features-for-amplify-ui-and-studio-you-should-try-today-4g86</link>
      <guid>https://forem.com/erikch/5-new-features-for-amplify-ui-and-studio-you-should-try-today-4g86</guid>
      <description>&lt;p&gt;For the last two years I've been working as a front-end engineer on the &lt;a href="https://ui.docs.amplify.aws/" rel="noopener noreferrer"&gt;AWS Amplify UI&lt;/a&gt; team. On that team I saw the creation of the &lt;a href="https://ui.docs.amplify.aws/react/connected-components/authenticator" rel="noopener noreferrer"&gt;Authenticator&lt;/a&gt; component for Vue, React and Angular. We created a components library for React and a complete theming system as well. It was a very busy two years!&lt;/p&gt;

&lt;p&gt;At the end of last year we launched several new exciting features! In this post I'll be going over each new feature we've recently launched, and how it can benefit you. &lt;/p&gt;

&lt;p&gt;Please, follow me on Twitter at &lt;a href="https://twitter.com/ErikCH" rel="noopener noreferrer"&gt;ErikCH&lt;/a&gt; for more updates on new Amplify features and if you have any questions!&lt;/p&gt;

&lt;p&gt;If you're new to Amplify and stumbled on the blog post, welcome! Feel free to check out my last post on Getting Started with Auth!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As of this writing the Account Settings, File Uploader, and React Native Authenticator features are all in developer preview.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Account Settings
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://ui.docs.amplify.aws/react/connected-components/account-settings" rel="noopener noreferrer"&gt;Account Settings&lt;/a&gt; connected component adds a straight forward way to change settings for users. For the preview launch we targeted deleting users and changing passwords for logged in users.&lt;/p&gt;

&lt;p&gt;Imagine a user is signed in and wants to delete their account. The Account Settings component can be added into any page to add this functionality in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;AccountSettings&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;@aws-amplify/ui-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;handleSuccess&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="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user has been successfully deleted&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AccountSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeleteUser&lt;/span&gt; &lt;span class="nx"&gt;onSuccess&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSuccess&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="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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejqkafkbzc5cyc7w1ywo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejqkafkbzc5cyc7w1ywo.png" alt="image--21-" width="800" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another common scenario is for users to change their account password, after signing in. The change password component can be added in with a few lines of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;AccountSettings&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;@aws-amplify/ui-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;handleSuccess&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="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password is successfully changed!&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AccountSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChangePassword&lt;/span&gt; &lt;span class="nx"&gt;onSuccess&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSuccess&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="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;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fogs07e04ww4udf0tixpf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fogs07e04ww4udf0tixpf.png" alt="image--22-" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each of these components can be &lt;a href="https://ui.docs.amplify.aws/react/connected-components/account-settings/delete-user#component-overrides" rel="noopener noreferrer"&gt;completly overriden and customized&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. File Uploader
&lt;/h2&gt;

&lt;p&gt;Uploading files to S3 has never been more straight forward than with the &lt;a href="https://ui.docs.amplify.aws/react/connected-components/storage/fileuploader" rel="noopener noreferrer"&gt;File Uploader&lt;/a&gt; connected component. This component adds a way to upload files to S3 by drag-and-drop or by clicking the &lt;code&gt;Browse files&lt;/code&gt; button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;FileUploader&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;@aws-amplify/ui-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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-amplify/ui-react/styles.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DefaultFileUploaderExample&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;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;FileUploader&lt;/span&gt;
      &lt;span class="nx"&gt;acceptedFileTypes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
      &lt;span class="nx"&gt;accessLevel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0476r3rhjispwh8ad2k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0476r3rhjispwh8ad2k.png" alt="Screen-Shot-2023-02-21-at-3.22.18-PM" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A file preview window will appear after a file is selected to allow for renaming, adding or removing files if needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furppawbpfou3nvctxfvp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furppawbpfou3nvctxfvp.png" alt="Screen-Shot-2023-02-21-at-3.24.38-PM" width="800" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As with many of our other connected components, we have included an extensive &lt;a href="https://ui.docs.amplify.aws/react/connected-components/storage/fileuploader#setting-limits" rel="noopener noreferrer"&gt;API&lt;/a&gt; that includes many ways to customize the File Uploader experience including limits on max number of files, max size, resuming of files, displaying image preview, and a lot more!&lt;/p&gt;

&lt;h2&gt;
  
  
  3. React Native Support For Authenticator
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://ui.docs.amplify.aws/react-native/connected-components/authenticator" rel="noopener noreferrer"&gt;Authenticator&lt;/a&gt; component adds a complete authentication flow to your application with minimal boilerplate. What that means is you can add the Authenticator to your application, and get a sign in, sign up, and federated identities all with a few lines of code! &lt;/p&gt;

&lt;p&gt;This component is now in developer preview for React Native. Like our other implementations of the Authenticator for web, we have included in React Native the same rich set of API configurations and settings. This includes changing sign up attributes, login mechanisms, and headless usage with &lt;a href="https://ui.docs.amplify.aws/react-native/connected-components/authenticator/customization#subcomponent-override-slots" rel="noopener noreferrer"&gt;sub component overrides&lt;/a&gt;! Sub component overrides just means that you can override any component inside the Authenticator to customize it to your liking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;Button&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;react-native&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;Amplify&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;aws-amplify&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;useAuthenticator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;withAuthenticator&lt;/span&gt;&lt;span class="p"&gt;,&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;@aws-amplify/ui-react-native&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;awsconfig&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;./aws-exports&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;Amplify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;awsconfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SignOutButton&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;signOut&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuthenticator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;onPress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;signOut&lt;/span&gt;&lt;span class="p"&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;Sign Out&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SignOutButton&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;withAuthenticator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&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://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vd71vajkgcjl04296ow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vd71vajkgcjl04296ow.png" alt="Screen-Shot-2023-02-21-at-3.36.29-PM" width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Form Builder
&lt;/h2&gt;

&lt;p&gt;A common use case in web development is creating forms and connecting those forms to backend data sources. The &lt;a href="https://docs.amplify.aws/console/formbuilder/overview/" rel="noopener noreferrer"&gt;Amplify Studio's Form Builder&lt;/a&gt; makes this chore a snap.&lt;/p&gt;

&lt;p&gt;With Form Builder you can connect any data model, any JSON object, or create forms from scratch using our visual builder. &lt;/p&gt;

&lt;p&gt;Let's imagine you want to create a form with a JSON object. You can paste the object into the builder and it will generate the form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;basics&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firstName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Wesley&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;emailAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wpeck@amazon.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;favoriteThings&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;animals&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cats&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Snakes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Quokka&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;month&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;December&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;enabled&lt;/span&gt;&lt;span class="dl"&gt;"&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;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft154vouoks58q5kd69g7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft154vouoks58q5kd69g7.png" alt="overview6" width="334" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can then download the form into your project, by using the &lt;code&gt;Amplify Pull&lt;/code&gt; command. We auto generate all the code for you using our Amplify UI Components library. You can then connect this form anywhere you want in your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Data Management
&lt;/h2&gt;

&lt;p&gt;Inside &lt;a href="https://docs.amplify.aws/console/" rel="noopener noreferrer"&gt;Amplify Studio&lt;/a&gt;, we added a new &lt;a href="https://docs.amplify.aws/console/data/content-management/" rel="noopener noreferrer"&gt;Data Management view&lt;/a&gt;. This gives you a tabular view of the backend data for an application. &lt;/p&gt;

&lt;p&gt;This can be used by technical and non-technical team members the abality to create and update an applications data in real-time instead of building admin views.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjjgh529xsebe7jq5485q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjjgh529xsebe7jq5485q.png" alt="cms" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I find this feature particularly helpful to seed my database for testing purposes, which is another feature of the Data Management view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;One of our biggest launches recently is the addition of &lt;a href="https://aws.amazon.com/blogs/mobile/amplify-next-js-13/" rel="noopener noreferrer"&gt;Next 12, 13 with SSR and ISR support&lt;/a&gt; for Amplify Hosting. &lt;/p&gt;

&lt;p&gt;We now support API routes, middleware, and image optimization! We've also improved hosting with faster builds, Amazon Cloudwatch integration with a better fully managed hosting infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We've been working hard on adding more features that you want! If you have an idea for a feature or a bug that needs fixing, add an &lt;a href="https://github.com/aws-amplify/amplify-ui/issues/new/choose" rel="noopener noreferrer"&gt;issue&lt;/a&gt;.  &lt;/p&gt;

</description>
      <category>ai</category>
      <category>bug</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Add Auth To Your Nuxt 3 App in Minutes With Amplify</title>
      <dc:creator>Erik Hanchett</dc:creator>
      <pubDate>Fri, 03 Feb 2023 23:19:42 +0000</pubDate>
      <link>https://forem.com/erikch/add-auth-to-your-nuxt-3-app-in-minutes-with-amplify-2d7j</link>
      <guid>https://forem.com/erikch/add-auth-to-your-nuxt-3-app-in-minutes-with-amplify-2d7j</guid>
      <description>&lt;p&gt;&lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;Nuxt&lt;/a&gt; is an intuitive framework that takes your Vue applications and brings it to a new level! It can take your Vue 2/3 app and add static site generation, and server side rendering! In addition, it supports API routes, file system routing, data fetching and more!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.amplify.aws/" rel="noopener noreferrer"&gt;Amplify&lt;/a&gt;, on the other hand, is a set of tools that allows full-stack web and mobile developers the ability to create and build apps using AWS services. &lt;/p&gt;

&lt;p&gt;With these two together you can build some really powerful apps!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to get started now? Check out my video and jump on in!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/46DxCr5w1u0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are We Building?
&lt;/h2&gt;

&lt;p&gt;Today we are building a Nuxt 3 application with authentication backed by Amplify!&lt;/p&gt;

&lt;p&gt;We'll imagine that we need our entire application protected, and only authorized users can access it. We'll allow users to login and create an account!&lt;/p&gt;

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

&lt;p&gt;We are going to assume you are starting a Nuxt app from scratch without any prior knowledge of Amplify. Feel free to jump to the next section if this is not the case!&lt;/p&gt;

&lt;p&gt;Let's begin by creating a brand new app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nuxi init nuxt3-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the app is generated change directory to the newly created folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;nuxt3-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the &lt;code&gt;aws-amplify&lt;/code&gt; and &lt;code&gt;@aws-amplify/ui-vue&lt;/code&gt; dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @aws-amplify/ui-vue aws-amplify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this is the first time using Amplify you'll need to install the &lt;a href="https://github.com/aws-amplify/amplify-cli" rel="noopener noreferrer"&gt;Amplify CLI&lt;/a&gt;. This tool will help us setup and add Amplify's services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @aws-amplify/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we'll run configure. This will connect our CLI tool with our AWS account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't have an AWS account? &lt;a href="https://aws.amazon.com/free" rel="noopener noreferrer"&gt;Sign up&lt;/a&gt; and get 12 months free!&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we'll run an init command to setup our project!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hit enter through all the defaults and you should be ready to go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Authentication
&lt;/h2&gt;

&lt;p&gt;Adding authentication to your application with Amplify only takes one command on the CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify add auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to follow the prompts. Choose &lt;code&gt;Email&lt;/code&gt; and the &lt;code&gt;Default configuration&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Do you want to use the default authentication and security configuration? Default configuration
Do you want to use the default authentication and security configuration? Default configuration
 Warning: you will not be able to edit these selections. 
 How &lt;span class="k"&gt;do &lt;/span&gt;you want &lt;span class="nb"&gt;users &lt;/span&gt;to be able to sign &lt;span class="k"&gt;in&lt;/span&gt;? Email
 Do you want to configure advanced settings? No, I am &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can push our changes to Amplify!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose &lt;code&gt;Y&lt;/code&gt;!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Are you sure you want to &lt;span class="k"&gt;continue&lt;/span&gt;? &lt;span class="o"&gt;(&lt;/span&gt;Y/n&lt;span class="o"&gt;)&lt;/span&gt; y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you look inside your folder structure in your Nuxt app, you may notice some &lt;a href="https://docs.amplify.aws/cli/reference/files/" rel="noopener noreferrer"&gt;new folders&lt;/a&gt;. Don't worry, these are configuration files for Amplify, we won't need to touch them for this tutorial. &lt;/p&gt;

&lt;p&gt;We are done with the command line for now. Let's setup the Nuxt app so it can use this new authentication resource.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nuxt Setup
&lt;/h2&gt;

&lt;p&gt;Let's jump into our &lt;code&gt;nuxt.config.ts&lt;/code&gt; file and add some configurations for Amplify.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// nuxt.config.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineNuxtConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./runtimeConfig&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./runtimeConfig.browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;vite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;define&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;window.global&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="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;p&gt;&lt;strong&gt;Nuxt uses &lt;code&gt;vite&lt;/code&gt; under the covers as a build tool. These options will help Nuxt and Vite properly build.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Let's create a plugin for Amplify. This plugin file will configure Amplify and make it available throughout the app.&lt;/p&gt;

&lt;p&gt;Create a new &lt;code&gt;plugins&lt;/code&gt; folder in the root of the project and add a &lt;code&gt;amplify.ts&lt;/code&gt; file to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;defineNuxtPlugin&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="s2"&gt;#app&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;Amplify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Auth&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="s2"&gt;aws-amplify&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;aws_exports&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../src/aws-exports&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;defineNuxtPlugin&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;nuxtApp&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;Amplify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aws_exports&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="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Auth&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;aws_exports&lt;/code&gt; file was generated after you ran &lt;code&gt;amplify push&lt;/code&gt;. This file holds all the keys needed for the Amplify JavaScript library to access all the resources you created. By adding the &lt;code&gt;provide&lt;/code&gt; you can now access auth with &lt;code&gt;$auth&lt;/code&gt; anywhere in your app.&lt;/p&gt;

&lt;p&gt;We are now ready to add the &lt;a href="https://ui.docs.amplify.aws/vue/connected-components/authenticator" rel="noopener noreferrer"&gt;Authenticator&lt;/a&gt; to our application. This will be in our &lt;code&gt;app.vue&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;Authenticator&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="s2"&gt;@aws-amplify/ui-vue&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="s2"&gt;@aws-amplify/ui-vue/styles.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Authenticator&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;v-slot=&lt;/span&gt;&lt;span class="s"&gt;"{ user, signOut }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello {{ user.username }}!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"signOut"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign Out&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Authenticator&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Don't forget to add the import for styles as seen above! Otherwise your Authenticator will not render properly.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the code example above anything between the opening and closing brackets inside the &lt;code&gt;template&lt;/code&gt; will only appear after the user is logged in. &lt;/p&gt;

&lt;h2&gt;
  
  
  Test It Out!
&lt;/h2&gt;

&lt;p&gt;Go ahead and start your Nuxt app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations! You now have created a Nuxt.js application with authentication!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk65uury9irysqixk0ngs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk65uury9irysqixk0ngs.png" alt="Image description" width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go ahead and try to create an account, and sign in. Make sure to put in a valid email address, so you can verify your account during the sign up process.&lt;/p&gt;

&lt;p&gt;After signing in you'll see a message like this!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feu5pfb2ry9bz1h5h811a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feu5pfb2ry9bz1h5h811a.png" alt="Image description" width="800" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post we've created a new Nuxt 3 application, added in Amplify with authentication, and setup our app to use Amplify with the Authenticator connected component!&lt;/p&gt;

&lt;p&gt;So your next question probably is, where do we go from here? Well, with Amplify setup we can now add in &lt;a href="https://docs.amplify.aws/cli/function/" rel="noopener noreferrer"&gt;authenticated apis&lt;/a&gt; backed by Lambda, &lt;a href="https://docs.amplify.aws/cli/storage/overview/" rel="noopener noreferrer"&gt;storage&lt;/a&gt;, and even &lt;a href="https://docs.amplify.aws/cli/graphql/overview/" rel="noopener noreferrer"&gt;AppSync&lt;/a&gt; our managed GraphQL service! &lt;/p&gt;

&lt;p&gt;Leave a comment below on what you'd like to learn next! Or tweet me at &lt;a href="https://twitter.com/erikch" rel="noopener noreferrer"&gt;@ErikCH&lt;/a&gt; with what you learned and I'll pick someone at random for a special prize!&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>amplify</category>
      <category>authentication</category>
    </item>
    <item>
      <title>React Signup/Login/Account Settings application With Amplify</title>
      <dc:creator>Erik Hanchett</dc:creator>
      <pubDate>Mon, 16 Jan 2023 17:59:18 +0000</pubDate>
      <link>https://forem.com/erikch/react-signuplogindelete-user-application-with-amplify-1722</link>
      <guid>https://forem.com/erikch/react-signuplogindelete-user-application-with-amplify-1722</guid>
      <description>&lt;p&gt;Hi, I’m Erik, I’m a front-end engineer on the &lt;a href="https://ui.docs.amplify.aws/" rel="noopener noreferrer"&gt;Amplify UI&lt;/a&gt; team and I wanted to share with you how to create a React application with authentication. Then we’ll look at how we can add in some nice features, like deleting users and updating passwords with our new Account Settings components.&lt;/p&gt;

&lt;p&gt;If you want to jump in right away, check out my YouTube video to get started below. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/CIUx6_gQyyE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Amplify?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.amplify.aws/" rel="noopener noreferrer"&gt;Amplify&lt;/a&gt; is a set of tools that allows full-stack web and mobile developers to create and build apps. It makes using AWS services, like our &lt;a href="https://aws.amazon.com/cognito/" rel="noopener noreferrer"&gt;Cognito&lt;/a&gt; identity and access management service, or our managed GraphQL service &lt;a href="https://aws.amazon.com/appsync/" rel="noopener noreferrer"&gt;AppSync&lt;/a&gt;, much simpler and straightforward to use.  &lt;/p&gt;

&lt;h2&gt;
  
  
  What Are We Building?
&lt;/h2&gt;

&lt;p&gt;Imagine you need to create a dashboard application, that only logged in users are able to access. Let's say you want to add a way for users to change their password after logging in, or completely delete their account. Let's take a look at that scenario!&lt;/p&gt;

&lt;p&gt;In this tutorial we’ll use our &lt;a href="https://ui.docs.amplify.aws/" rel="noopener noreferrer"&gt;Amplify UI component library&lt;/a&gt;, with the &lt;a href="https://ui.docs.amplify.aws/react/connected-components/authenticator" rel="noopener noreferrer"&gt;Authenticator&lt;/a&gt; and &lt;a href="https://ui.docs.amplify.aws/react/connected-components/account-settings" rel="noopener noreferrer"&gt;Account Settings&lt;/a&gt; connected components to create a full stack authenticated application. &lt;/p&gt;

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

&lt;p&gt;To begin we’ll create a new application. We'll use &lt;a href="https://create-react-app.dev/" rel="noopener noreferrer"&gt;Create React App&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;npx create-react-app my-auth-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd like to use Vite instead you can run the following 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 vite@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;If you are using Vite, make sure to follow these additional &lt;a href="https://ui.docs.amplify.aws/react/getting-started/troubleshooting#vite" rel="noopener noreferrer"&gt;steps&lt;/a&gt; to get setup!&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-auth-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next up, let’s install a few libraries to get up and running!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i aws-amplify @aws-amplify/ui-react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don’t already have it installed, we’ll be using Amplify CLI to create our app. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you’re familiar with &lt;a href="https://github.com/aws-amplify/amplify-cli" rel="noopener noreferrer"&gt;Amplify CLI&lt;/a&gt;, you can skip this part and jump over to the init command below. You can alternately use &lt;a href="https://console.aws.amazon.com/amplify" rel="noopener noreferrer"&gt;Amplify Studio&lt;/a&gt; to setup your backend.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; @aws-amplify/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s setup Amplify CLI! Run the configure command, and follow the prompts. It will make you log into your AWS console and create a user. If you need to, you can sign up for a &lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;free AWS account&lt;/a&gt;. This should only take a few minutes. You can click next through all the prompts to continue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s configure Amplify for our app. Run the init command and hit enter through the prompts. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Having a problem? Check out the official &lt;a href="https://docs.amplify.aws/start/getting-started/setup/q/integration/react/#create-a-new-react-app" rel="noopener noreferrer"&gt;getting started guide&lt;/a&gt;.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s add auth, run this command next. Choose email as your login type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify add auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s push the changes! Run the push command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;amplify push 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding Authentication To Your React Application
&lt;/h2&gt;

&lt;p&gt;Now that everything is setup, we can dive into code and add React!&lt;/p&gt;

&lt;p&gt;Fire up VSCode, or your editor of choice and open the &lt;code&gt;index&lt;/code&gt; file and add an import for &lt;code&gt;aws_exports&lt;/code&gt; Amplify, and the styles! Then make sure to add a configure command. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;aws-exports&lt;/code&gt; file is generated after adding auth to your app and pushing your changes to AWS. You may also notice a &lt;code&gt;amplify&lt;/code&gt; folder. This will have all your configuration files in it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;Amplify&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="s2"&gt;aws-amplify&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;aws_exports&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./aws-exports&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;@aws-amplify/ui-react/styles.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Amplify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aws_exports&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside your &lt;code&gt;App&lt;/code&gt; file import the &lt;code&gt;Authenticator&lt;/code&gt;. You’ll be surrounding your application with it. We’ll render a &lt;code&gt;signOut&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; so the user information can be displayed and a logout button can be added.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Button is a component from our &lt;a href="https://ui.docs.amplify.aws/react/components/button" rel="noopener noreferrer"&gt;UI component library&lt;/a&gt;!&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;Authenticator&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;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-amplify/ui-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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;Authenticator&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;signOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&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;main&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;h1&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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&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;/h1&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;Button&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;signOut&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;Sign&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&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;/main&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;/Authenticator&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;Go ahead and start your application and you’ll see a login page.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc20c4lykfe2oc3iz7ewu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc20c4lykfe2oc3iz7ewu.png" alt="Image description" width="800" height="609"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here you can create an account using the &lt;code&gt;Create Account&lt;/code&gt; tab and login. It will then prompt you to check your email to enter a code in. Go ahead and try it out, create a few users and after logging in, and try to log out.&lt;/p&gt;

&lt;p&gt;Need to customize the look and feel? Check out out our &lt;a href="https://ui.docs.amplify.aws/react/connected-components/authenticator/customization" rel="noopener noreferrer"&gt;customization docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Account Settings
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;The Account Settings components are currently in Developer Preview! We are working hard on finalizing it soon.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://ui.docs.amplify.aws/react/connected-components/account-settings" rel="noopener noreferrer"&gt;Account Settings&lt;/a&gt; component allows an already logged in user to change passwords as well as deleting a user. Let’s begin to see how to change a user. &lt;/p&gt;

&lt;h3&gt;
  
  
  Change Password
&lt;/h3&gt;

&lt;p&gt;Inside the &lt;code&gt;App&lt;/code&gt; file in between the opening and closing brackets of the &lt;code&gt;Authenticator&lt;/code&gt; add the &lt;code&gt;AccountSettings.ChangePassword&lt;/code&gt; component. &lt;/p&gt;

&lt;p&gt;We’ll also add a &lt;code&gt;handleSuccess&lt;/code&gt; callback that will be triggered after a change password action occurs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;Authenticator&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="nx"&gt;AccountSettings&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;@aws-amplify/ui-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;handleSuccess&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="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password is successfully changed!&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;...&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AccountSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ChangePassword&lt;/span&gt; &lt;span class="nx"&gt;onSuccess&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSuccess&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="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;p&gt;After logging in you’ll see a page like this. Go ahead and change your password. You can see from the console that the password was changed. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nu4y7fnm7vbv799dudj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nu4y7fnm7vbv799dudj.png" alt="Image description" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Logout and back in again and test the functionality with your new password.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delete User
&lt;/h3&gt;

&lt;p&gt;Go back into your &lt;code&gt;App&lt;/code&gt; file and remove the change password component. Let’s look at the &lt;code&gt;AccountSettings.DeleteUser&lt;/code&gt; component instead. We’ll also add a &lt;code&gt;handleSuccess&lt;/code&gt; callback that will be called as soon as the user is deleted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;Authenticator&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="nx"&gt;AccountSettings&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;@aws-amplify/ui-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;handleSuccess&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="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user has been successfully deleted&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;...&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AccountSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeleteUser&lt;/span&gt; &lt;span class="nx"&gt;onSuccess&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSuccess&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="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;p&gt;Log into one of your existing accounts and you should see this page. Go ahead and delete a user, it will log you out. Check the console and you’ll see the success message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnn6x9xyyh9a9h6g8nzgo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnn6x9xyyh9a9h6g8nzgo.png" alt="Image description" width="800" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Customization
&lt;/h3&gt;

&lt;p&gt;A powerful feature of the Account Settings component are its levels of customization. Beyond CSS and &lt;a href="https://ui.docs.amplify.aws/react/theming" rel="noopener noreferrer"&gt;design tokens&lt;/a&gt;, you can override nearly every component with your own look and feel.&lt;/p&gt;

&lt;p&gt;Let’s override the delete button. In the &lt;code&gt;AccountSettings.DeleteUser&lt;/code&gt; component add a &lt;code&gt;components&lt;/code&gt; prop. Inside that props you’ll create a &lt;code&gt;DeleteButton&lt;/code&gt; object. This will override the delete button on the &lt;code&gt;DeleteUser&lt;/code&gt; component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;components&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;DeleteButton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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;AccountSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeleteUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeleteButton&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&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;Custom&lt;/span&gt; &lt;span class="nx"&gt;Delete&lt;/span&gt; &lt;span class="nx"&gt;Account&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/AccountSettings.DeleteUser.DeleteButton&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="k"&gt;return &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;AccountSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeleteUser&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;components&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="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;p&gt;You’ll see a page that looks like this, with the new deleted button component after logging back in!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feed042a6xvmsq7nop0ke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feed042a6xvmsq7nop0ke.png" alt="Image description" width="800" height="95"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and Next Steps!
&lt;/h2&gt;

&lt;p&gt;In this tutorial we learned how to add authentication to our React app. We’ve learned about the new Account Settings connected component and how to use it to delete, and change password. &lt;/p&gt;

&lt;p&gt;This is just the beginning of what you can do with the Amplify. Check out our &lt;a href="https://ui.docs.amplify.aws/react/guides/auth-protected" rel="noopener noreferrer"&gt;protected routes&lt;/a&gt; and our &lt;a href="https://docs.amplify.aws/lib/graphqlapi/getting-started/q/platform/js/" rel="noopener noreferrer"&gt;AppSync tutorial&lt;/a&gt; for some more information!&lt;/p&gt;

&lt;p&gt;If you’ve read all the way to the bottom, leave a comment and let me know what you’d like to see next in the world of Amplify! Thanks!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>authentication</category>
      <category>react</category>
      <category>amplify</category>
    </item>
  </channel>
</rss>
