Repository: phoenixframework/phoenix
Pull request #5266 (2023-02-11 17:56:16)

Update year on LiveView course link


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 531f46a3d..aa8b910f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,12 @@ This feature replaces the `Helpers` module generated in your Phoenix router, but
 will continue to work and be generated. You can disable router helpers by passing the
 `helpers: false` option to `use Phoenix.Router`.
 
+## 1.7.0-rc.3 (2023-02-15)
+
+### Enhancements
+  * Use stream based collections for `phx.gen.live` generators
+  * Update `phx.gen.live` generators to use `Phoenix.Component.to_form`
+
 ## 1.7.0-rc.2 (2023-01-13)
 
 ### Bug Fixes
diff --git a/assets/package-lock.json b/assets/package-lock.json
index 553f86253..ff1a35d60 100644
--- a/assets/package-lock.json
+++ b/assets/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "phoenix",
-  "version": "1.7.0-rc.2",
+  "version": "1.7.0-rc.3",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "phoenix",
-      "version": "1.7.0-rc.2",
+      "version": "1.7.0-rc.3",
       "license": "MIT",
       "devDependencies": {
         "@babel/cli": "7.14.3",
diff --git a/assets/package.json b/assets/package.json
index 59377ecdd..473fa9272 100644
--- a/assets/package.json
+++ b/assets/package.json
@@ -1,6 +1,6 @@
 {
   "name": "phoenix",
-  "version": "1.7.0-rc.2",
+  "version": "1.7.0-rc.3",
   "description": "The official JavaScript client for the Phoenix web framework.",
   "license": "MIT",
   "main": "./assets/js/phoenix/index.js",
diff --git a/guides/authentication/mix_phx_gen_auth.md b/guides/authentication/mix_phx_gen_auth.md
index 7832be7e8..4597b8b87 100644
--- a/guides/authentication/mix_phx_gen_auth.md
+++ b/guides/authentication/mix_phx_gen_auth.md
@@ -10,9 +10,17 @@ Let's start by running the following command from the root of our app (or `apps/
 
 ```console
 $ mix phx.gen.auth Accounts User users
+
+An authentication system can be created in two different ways:
+- Using Phoenix.LiveView (default)
+- Using Phoenix.Controller only
+
+Do you want to create a LiveView based authentication system? [Y/n] Y
 ```
 
-This creates an `Accounts` context with an `Accounts.User` schema module. The final argument is the plural version of the schema module, which is used for generating database table names and route paths. The `mix phx.gen.auth` generator is similar to `mix phx.gen.html` except it does not accept a list of additional fields to add to the schema, and it generates many more context functions.
+The authentication generators support Phoenix LiveView, for enhanced UX, so we'll answer `Y` here. You may also answer `n` for a controller based authentication system.
+
+Either approach will create an `Accounts` context with an `Accounts.User` schema module. The final argument is the plural version of the schema module, which is used for generating database table names and route paths. The `mix phx.gen.auth` generator is similar to `mix phx.gen.html` except it does not accept a list of additional fields to add to the schema, and it generates many more context functions.
 
 Since this generator installed additional dependencies in `mix.exs`, let's fetch those:
 
diff --git a/guides/contexts.md b/guides/contexts.md
index 98529eed0..765b7f666 100644
--- a/guides/contexts.md
+++ b/guides/contexts.md
@@ -137,12 +137,12 @@ defmodule HelloWeb.ProductController do
 
   def index(conn, _params) do
     products = Catalog.list_products()
-    render(conn, "index.html", products: products)
+    render(conn, :index, products: products)
   end
 
   def new(conn, _params) do
     changeset = Catalog.change_product(%Product{})
-    render(conn, "new.html", changeset: changeset)
+    render(conn, :new, changeset: changeset)
   end
 
   def create(conn, %{"product" => product_params}) do
@@ -153,13 +153,13 @@ defmodule HelloWeb.ProductController do
         |> redirect(to: ~p"/products/#{product}")
 
       {:error, %Ecto.Changeset{} = changeset} ->
-        render(conn, "new.html", changeset: changeset)
+        render(conn, :new, changeset: changeset)
     end
   end
 
   def show(conn, %{"id" => id}) do
     product = Catalog.get_product!(id)
-    render(conn, "show.html", product: product)
+    render(conn, :show, product: product)
   end
   ...
 end
@@ -304,7 +304,7 @@ With our context function in place, let's make use of it in our product controll
       |> Catalog.get_product!()
       |> Catalog.inc_page_views()
 
-    render(conn, "show.html", product: product)
+    render(conn, :show, product: product)
   end
 ```
 
@@ -865,7 +865,7 @@ Next, we wrote our new `add_item_to_cart/2` function which accepts a cart struct
 
 Finally, we implemented `remove_item_from_cart/2` where we simply issue a `Repo.delete_all` call with a query to delete the cart item in our cart that matches the product ID. Finally, we reload the cart contents by calling `reload_cart/1`.
 
-With our new cart functions in place, we can now expose the "Add to cart" button on the product catalog show page. Open up your template in `lib/hello_web/templates/product/show.html.heex` and make the following changes:
+With our new cart functions in place, we can now expose the "Add to cart" button on the product catalog show page. Open up your template in `lib/hello_web/controllers/product_html/show.html.heex` and make the following changes:
 
 ```diff
 <h1>Show Product</h1>
@@ -911,7 +911,7 @@ defmodule HelloWeb.CartController do
   alias Hello.ShoppingCart
 
   def show(conn, _params) do
-    render(conn, "show.html", changeset: ShoppingCart.change_cart(conn.assigns.cart))
+    render(conn, :show, changeset: ShoppingCart.change_cart(conn.assigns.cart))
   end
 end
 ```
diff --git a/guides/introduction/community.md b/guides/introduction/community.md
index 10fef641f..1d47514b9 100644
--- a/guides/introduction/community.md
+++ b/guides/introduction/community.md
@@ -36,7 +36,7 @@ There are a number of places to connect with community members at all experience
 
 ## Screencasts/Courses
 
-  * [Phoenix LiveView Free Course (by The Pragmatic Studio - 2020)](https://pragmaticstudio.com/courses/phoenix-liveview)
+  * [Phoenix LiveView Free Course (by The Pragmatic Studio - 2023)](https://pragmaticstudio.com/courses/phoenix-liveview)
 
   * [Groxio LiveView: Self Study Program (by Bruce Tate - 2020)](https://grox.io/language/liveview/course)
 
diff --git a/installer/mix.exs b/installer/mix.exs
index 2112b924c..880d9f045 100644
--- a/installer/mix.exs
+++ b/installer/mix.exs
@@ -6,7 +6,7 @@ end
 defmodule Phx.New.MixProject do
   use Mix.Project
 
-  @version "1.7.0-rc.2"
+  @version "1.7.0-rc.3"
   @scm_url "https://github.com/phoenixframework/phoenix"
 
   # If the elixir requirement is updated, we need to update:
diff --git a/installer/mix.lock b/installer/mix.lock
index 843b4accd..f534fd9d4 100644
--- a/installer/mix.lock
+++ b/installer/mix.lock
@@ -1,9 +1,9 @@
 %{
   "earmark": {:hex, :earmark, "1.4.10", "bddce5e8ea37712a5bfb01541be8ba57d3b171d3fa4f80a0be9bcf1db417bcaf", [:mix], [{:earmark_parser, ">= 1.4.10", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "12dbfa80810478e521d3ffb941ad9fbfcbbd7debe94e1341b4c4a1b2411c1c27"},
-  "earmark_parser": {:hex, :earmark_parser, "1.4.12", "b245e875ec0a311a342320da0551da407d9d2b65d98f7a9597ae078615af3449", [:mix], [], "hexpm", "711e2cc4d64abb7d566d43f54b78f7dc129308a63bc103fbd88550d2174b3160"},
-  "ex_doc": {:hex, :ex_doc, "0.24.1", "15673de99154f93ca7f05900e4e4155ced1ee0cd34e0caeee567900a616871a4", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "07972f17bdf7dc7b5bd76ec97b556b26178ed3f056e7ec9288eb7cea7f91cce2"},
-  "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
-  "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"},
+  "earmark_parser": {:hex, :earmark_parser, "1.4.30", "0b938aa5b9bafd455056440cdaa2a79197ca5e693830b4a982beada840513c5f", [:mix], [], "hexpm", "3b5385c2d36b0473d0b206927b841343d25adb14f95f0110062506b300cd5a1b"},
+  "ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"},
+  "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
+  "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
   "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
-  "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},
+  "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
 }
diff --git a/installer/templates/phx_assets/app.js b/installer/templates/phx_assets/app.js
index ec7623990..4c1063740 100644
--- a/installer/templates/phx_assets/app.js
+++ b/installer/templates/phx_assets/app.js
@@ -27,7 +27,7 @@ import "phoenix_html"
 
 // Show progress bar on live navigation and form submits
 <%= @live_comment %>topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
-<%= @live_comment %>window.addEventListener("phx:page-loading-start", _info => topbar.show(200))
+<%= @live_comment %>window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
 <%= @live_comment %>window.addEventListener("phx:page-loading-stop", _info => topbar.hide())
 
 // connect if there are any LiveViews on the page
diff --git a/installer/templates/phx_single/mix.exs b/installer/templates/phx_single/mix.exs
index 078dbab7c..0ccf76050 100644
--- a/installer/templates/phx_single/mix.exs
+++ b/installer/templates/phx_single/mix.exs
@@ -42,7 +42,7 @@ defmodule <%= @app_module %>.MixProject do
       {<%= inspect @adapter_app %>, ">= 0.0.0"},<% end %><%= if @html do %>
       {:phoenix_html, "~> 3.3"},
       {:phoenix_live_reload, "~> 1.2", only: :dev},
-      {:phoenix_live_view, "~> 0.18.12"},
+      {:phoenix_live_view, "~> 0.18.14"},
       {:heroicons, "~> 0.5"},
       {:floki, ">= 0.30.0", only: :test},<% end %><%= if @dashboard do %>
       {:phoenix_live_dashboard, "~> 0.7.2"},<% end %><%= if @assets do %>
diff --git a/installer/templates/phx_umbrella/apps/app_name_web/mix.exs b/installer/templates/phx_umbrella/apps/app_name_web/mix.exs
index bf1ef4728..196b3dabe 100644
--- a/installer/templates/phx_umbrella/apps/app_name_web/mix.exs
+++ b/installer/templates/phx_umbrella/apps/app_name_web/mix.exs
@@ -40,7 +40,7 @@ defmodule <%= @web_namespace %>.MixProject do
       {:phoenix_ecto, "~> 4.4"},<% end %><%= if @html do %>
       {:phoenix_html, "~> 3.3"},
       {:phoenix_live_reload, "~> 1.2", only: :dev},
-      {:phoenix_live_view, "~> 0.18.12"},
+      {:phoenix_live_view, "~> 0.18.14"},
       {:heroicons, "~> 0.5"},
       {:floki, ">= 0.30.0", only: :test},<% end %><%= if @dashboard do %>
       {:phoenix_live_dashboard, "~> 0.7.2"},<% end %><%= if @assets do %>
diff --git a/installer/templates/phx_web/components/core_components.ex b/installer/templates/phx_web/components/core_components.ex
index 43aa2ff7c..4dfaaef1f 100644
--- a/installer/templates/phx_web/components/core_components.ex
+++ b/installer/templates/phx_web/components/core_components.ex
@@ -206,7 +206,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
 
   ## Examples
 
-      <.simple_form phx-change="validate" phx-submit="save">
+      <.simple_form for={@form} phx-change="validate" phx-submit="save">
         <.input field={@form[:email]} label="Email"/>
         <.input field={@form[:username]} label="Username" />
         <:actions>
@@ -214,7 +214,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
         </:actions>
       </.simple_form>
   """
-  attr :for, :any, default: nil, doc: "the datastructure for the form"
+  attr :for, :any, required: true, doc: "the datastructure for the form"
   attr :as, :any, default: nil, doc: "the server side parameter to collect all input under"
 
   attr :rest, :global,
@@ -461,9 +461,13 @@ defmodule <%= @web_namespace %>.CoreComponents do
       </.table>
   """
   attr :id, :string, required: true
+  attr :rows, :list, required: true
   attr :row_id, :any, default: nil, doc: "the function for generating the row id"
   attr :row_click, :any, default: nil, doc: "the function for handling phx-click on each row"
-  attr :rows, :list, required: true
+
+  attr :row_item, :any,
+    default: &Function.identity/1,
+    doc: "the function for mapping each row before calling the :col and :action slots"
 
   slot :col, required: true do
     attr :label, :string
@@ -472,8 +476,13 @@ defmodule <%= @web_namespace %>.CoreComponents do
   slot :action, doc: "the slot for showing user actions in the last table column"
 
   def table(assigns) do
+    assigns =
+      with %{rows: %Phoenix.LiveView.LiveStream{}} <- assigns do
+        assign(assigns, row_id: assigns.row_id || fn {id, _item} -> id end)
+      end
+
     ~H"""
-    <div id={@id} class="overflow-y-auto px-4 sm:overflow-visible sm:px-0">
+    <div class="overflow-y-auto px-4 sm:overflow-visible sm:px-0">
       <table class="mt-11 w-[40rem] sm:w-full">
         <thead class="text-left text-[0.8125rem] leading-6 text-zinc-500">
           <tr>
@@ -481,12 +490,12 @@ defmodule <%= @web_namespace %>.CoreComponents do
             <th class="relative p-0 pb-4"><span class="sr-only"><%= if @gettext do %><%%= gettext("Actions") %><% else %>Actions<% end %></span></th>
           </tr>
         </thead>
-        <tbody class="relative divide-y divide-zinc-100 border-t border-zinc-200 text-sm leading-6 text-zinc-700">
-          <tr
-            :for={row <- @rows}
-            id={@row_id && @row_id.(row)}
-            class="group hover:bg-zinc-50"
-          >
+        <tbody
+          id={@id}
+          phx-update={match?(%Phoenix.LiveView.LiveStream{}, @rows) && "stream"}
+          class="relative divide-y divide-zinc-100 border-t border-zinc-200 text-sm leading-6 text-zinc-700"
+        >
+          <tr :for={row <- @rows} id={@row_id && @row_id.(row)} class="group hover:bg-zinc-50">
             <td
               :for={{col, i} <- Enum.with_index(@col)}
               phx-click={@row_click && @row_click.(row)}
@@ -495,7 +504,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
               <div class="block py-4 pr-6">
                 <span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-zinc-50 sm:rounded-l-xl" />
                 <span class={["relative", i == 0 && "font-semibold text-zinc-900"]}>
-                  <%%= render_slot(col, row) %>
+                  <%%= render_slot(col, @row_item.(row)) %>
                 </span>
               </div>
             </td>
@@ -506,7 +515,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
                   :for={action <- @action}
                   class="relative ml-4 font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
                 >
-                  <%%= render_slot(action, row) %>
+                  <%%= render_slot(action, @row_item.(row)) %>
                 </span>
               </div>
             </td>
diff --git a/mix.exs b/mix.exs
index 6b9a37754..a9a880e2d 100644
--- a/mix.exs
+++ b/mix.exs
@@ -8,7 +8,7 @@ defmodule Phoenix.MixProject do
     end
   end
 
-  @version "1.7.0-rc.2"
+  @version "1.7.0-rc.3"
   @scm_url "https://github.com/phoenixframework/phoenix"
 
   # If the elixir requirement is updated, we need to make the installer
diff --git a/package.json b/package.json
index 86388e50b..e2a28c5aa 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "phoenix",
-  "version": "1.7.0-rc.2",
+  "version": "1.7.0-rc.3",
   "description": "The official JavaScript client for the Phoenix web framework.",
   "license": "MIT",
   "module": "./priv/static/phoenix.mjs",
diff --git a/priv/templates/phx.gen.auth/confirmation_edit.html.heex b/priv/templates/phx.gen.auth/confirmation_edit.html.heex
index 36e5923be..e52ec2680 100644
--- a/priv/templates/phx.gen.auth/confirmation_edit.html.heex
+++ b/priv/templates/phx.gen.auth/confirmation_edit.html.heex
@@ -1,7 +1,7 @@
 <div class="mx-auto max-w-sm">
   <.header class="text-center">Confirm account</.header>
 
-  <.simple_form :let={_f} as={:<%= schema.singular %>} action={~p"<%= schema.route_prefix %>/confirm/#{@token}"}>
+  <.simple_form for={@conn.params["<%= schema.singular %>"]} as={:<%= schema.singular %>} action={~p"<%= schema.route_prefix %>/confirm/#{@token}"}>
     <:actions>
       <.button class="w-full">Confirm my account</.button>
     </:actions>
diff --git a/priv/templates/phx.gen.auth/confirmation_instructions_live.ex b/priv/templates/phx.gen.auth/confirmation_instructions_live.ex
index 55da944bd..bef5fd6db 100644
--- a/priv/templates/phx.gen.auth/confirmation_instructions_live.ex
+++ b/priv/templates/phx.gen.auth/confirmation_instructions_live.ex
@@ -7,7 +7,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
     ~H"""
     <.header>Resend confirmation instructions</.header>
 
-    <.simple_form id="resend_confirmation_form" phx-submit="send_instructions">
+    <.simple_form for={@form} id="resend_confirmation_form" phx-submit="send_instructions">
       <.input field={@form[:email]} type="email" label="Email" required />
       <:actions>
         <.button phx-disable-with="Sending...">Resend confirmation instructions</.button>
diff --git a/priv/templates/phx.gen.auth/confirmation_live.ex b/priv/templates/phx.gen.auth/confirmation_live.ex
index c316a0cfd..eb6ff4879 100644
--- a/priv/templates/phx.gen.auth/confirmation_live.ex
+++ b/priv/templates/phx.gen.auth/confirmation_live.ex
@@ -8,7 +8,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
     <div class="mx-auto max-w-sm">
       <.header class="text-center">Confirm Account</.header>
 
-      <.simple_form id="confirmation_form" phx-submit="confirm_account">
+      <.simple_form for={@form} id="confirmation_form" phx-submit="confirm_account">
         <.input field={@form[:token]} type="hidden" />
         <:actions>
           <.button phx-disable-with="Confirming..." class="w-full">Confirm my account</.button>
@@ -25,7 +25,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
   end
 
   def mount(%{"token" => token}, _session, socket) do
-    form = to_form(%{"token" => token}, as: "user")
+    form = to_form(%{"token" => token}, as: "<%= schema.singular %>")
     {:ok, assign(socket, form: form), temporary_assigns: [form: nil]}
   end
 
diff --git a/priv/templates/phx.gen.auth/confirmation_new.html.heex b/priv/templates/phx.gen.auth/confirmation_new.html.heex
index ca377cf27..85789ee72 100644
--- a/priv/templates/phx.gen.auth/confirmation_new.html.heex
+++ b/priv/templates/phx.gen.auth/confirmation_new.html.heex
@@ -1,6 +1,6 @@
 <.header>Resend confirmation instructions</.header>
 
-<.simple_form :let={f} as={:<%= schema.singular %>} action={~p"<%= schema.route_prefix %>/confirm"}>
+<.simple_form :let={f} for={@conn.params["<%= schema.singular %>"]} as={:<%= schema.singular %>} action={~p"<%= schema.route_prefix %>/confirm"}>
   <.input field={f[:email]} type="email" label="Email" required />
   <:actions>
     <.button>Resend confirmation instructions</.button>
diff --git a/priv/templates/phx.gen.auth/forgot_password_live.ex b/priv/templates/phx.gen.auth/forgot_password_live.ex
index 87b4102ed..862160186 100644
--- a/priv/templates/phx.gen.auth/forgot_password_live.ex
+++ b/priv/templates/phx.gen.auth/forgot_password_live.ex
@@ -11,7 +11,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
         <:subtitle>We'll send a password reset link to your inbox</:subtitle>
       </.header>
 
-      <.simple_form id="reset_password_form" phx-submit="send_email">
+      <.simple_form for={@form} id="reset_password_form" phx-submit="send_email">
         <.input field={@form[:email]} type="email" placeholder="Email" required />
         <:actions>
           <.button phx-disable-with="Sending..." class="w-full">
diff --git a/priv/templates/phx.gen.auth/login_live.ex b/priv/templates/phx.gen.auth/login_live.ex
index 4c0ba149b..187ba426e 100644
--- a/priv/templates/phx.gen.auth/login_live.ex
+++ b/priv/templates/phx.gen.auth/login_live.ex
@@ -15,11 +15,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
         </:subtitle>
       </.header>
 
-      <.simple_form
-        id="login_form"
-        action={~p"<%= schema.route_prefix %>/log_in"}
-        phx-update="ignore"
-      >
+      <.simple_form for={@form} id="login_form" action={~p"<%= schema.route_prefix %>/log_in"} phx-update="ignore">
         <.input field={@form[:email]} type="email" label="Email" required />
         <.input field={@form[:password]} type="password" label="Password" required />
 
diff --git a/priv/templates/phx.gen.auth/registration_live.ex b/priv/templates/phx.gen.auth/registration_live.ex
index f439e6a58..98aa6d1f3 100644
--- a/priv/templates/phx.gen.auth/registration_live.ex
+++ b/priv/templates/phx.gen.auth/registration_live.ex
@@ -19,6 +19,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
       </.header>
 
       <.simple_form
+        for={@form}
         id="registration_form"
         phx-submit="save"
         phx-change="validate"
diff --git a/priv/templates/phx.gen.auth/reset_password_live.ex b/priv/templates/phx.gen.auth/reset_password_live.ex
index 05fc6f1f4..84d1a3f0c 100644
--- a/priv/templates/phx.gen.auth/reset_password_live.ex
+++ b/priv/templates/phx.gen.auth/reset_password_live.ex
@@ -9,6 +9,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
       <.header class="text-center">Reset Password</.header>
 
       <.simple_form
+        for={@form}
         id="reset_password_form"
         phx-submit="reset_password"
         phx-change="validate"
diff --git a/priv/templates/phx.gen.auth/reset_password_new.html.heex b/priv/templates/phx.gen.auth/reset_password_new.html.heex
index 3f17fc684..ee1a2ff34 100644
--- a/priv/templates/phx.gen.auth/reset_password_new.html.heex
+++ b/priv/templates/phx.gen.auth/reset_password_new.html.heex
@@ -4,7 +4,7 @@
     <:subtitle>We'll send a password reset link to your inbox</:subtitle>
   </.header>
 
-  <.simple_form :let={f} as={:<%= schema.singular %>} action={~p"<%= schema.route_prefix %>/reset_password"}>
+  <.simple_form :let={f} for={@conn.params["<%= schema.singular %>"]} as={:<%= schema.singular %>} action={~p"<%= schema.route_prefix %>/reset_password"}>
     <.input field={f[:email]} type="email" placeholder="Email" required />
     <:actions>
       <.button phx-disable-with="Sending..." class="w-full">
diff --git a/priv/templates/phx.gen.auth/session_new.html.heex b/priv/templates/phx.gen.auth/session_new.html.heex
index 98f6c5f4e..e2be9d413 100644
--- a/priv/templates/phx.gen.auth/session_new.html.heex
+++ b/priv/templates/phx.gen.auth/session_new.html.heex
@@ -10,7 +10,7 @@
     </:subtitle>
   </.header>
 
-  <.simple_form :let={f} for={@conn} action={~p"<%= schema.route_prefix %>/log_in"} as={:<%= schema.singular %>}>
+  <.simple_form :let={f} for={@conn.params["<%= schema.singular %>"]} as={:<%= schema.singular %>} action={~p"<%= schema.route_prefix %>/log_in"}>
     <.error :if={@error_message}><%%= @error_message %></.error>
 
     <.input field={f[:email]} type="email" label="Email" required />
diff --git a/priv/templates/phx.gen.auth/settings_live.ex b/priv/templates/phx.gen.auth/settings_live.ex
index 87031ca4b..e59b7f67e 100644
--- a/priv/templates/phx.gen.auth/settings_live.ex
+++ b/priv/templates/phx.gen.auth/settings_live.ex
@@ -8,6 +8,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
     <.header>Change Email</.header>
 
     <.simple_form
+      for={@email_form}
       id="email_form"
       phx-submit="update_email"
       phx-change="validate_email"
@@ -30,6 +31,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
     <.header>Change Password</.header>
 
     <.simple_form
+      for={@password_form}
       id="password_form"
       action={~p"<%= schema.route_prefix %>/log_in?_action=password_updated"}
       method="post"
@@ -39,7 +41,11 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
     >
       <.input field={@password_form[:email]} type="hidden" value={@current_email} />
       <.input field={@password_form[:password]} type="password" label="New password" required />
-      <.input field={@password_form[:password_confirmation]} type="password" label="Confirm new password" />
+      <.input
+        field={@password_form[:password_confirmation]}
+        type="password"
+        label="Confirm new password"
+      />
       <.input
         field={@password_form[:current_password]}
         name="current_password"
@@ -88,6 +94,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
 
   def handle_event("validate_email", params, socket) do
     %{"current_password" => password, "<%= schema.singular %>" => <%= schema.singular %>_params} = params
+
     email_form =
       socket.assigns.current_<%= schema.singular %>
       |> <%= inspect context.alias %>.change_<%= schema.singular %>_email(<%= schema.singular %>_params)
@@ -119,6 +126,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
 
   def handle_event("validate_password", params, socket) do
     %{"current_password" => password, "<%= schema.singular %>" => <%= schema.singular %>_params} = params
+
     password_form =
       socket.assigns.current_<%= schema.singular %>
       |> <%= inspect context.alias %>.change_<%= schema.singular %>_password(<%= schema.singular %>_params)
diff --git a/priv/templates/phx.gen.live/core_components.ex b/priv/templates/phx.gen.live/core_components.ex
index 43aa2ff7c..4dfaaef1f 100644
--- a/priv/templates/phx.gen.live/core_components.ex
+++ b/priv/templates/phx.gen.live/core_components.ex
@@ -206,7 +206,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
 
   ## Examples
 
-      <.simple_form phx-change="validate" phx-submit="save">
+      <.simple_form for={@form} phx-change="validate" phx-submit="save">
         <.input field={@form[:email]} label="Email"/>
         <.input field={@form[:username]} label="Username" />
         <:actions>
@@ -214,7 +214,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
         </:actions>
       </.simple_form>
   """
-  attr :for, :any, default: nil, doc: "the datastructure for the form"
+  attr :for, :any, required: true, doc: "the datastructure for the form"
   attr :as, :any, default: nil, doc: "the server side parameter to collect all input under"
 
   attr :rest, :global,
@@ -461,9 +461,13 @@ defmodule <%= @web_namespace %>.CoreComponents do
       </.table>
   """
   attr :id, :string, required: true
+  attr :rows, :list, required: true
   attr :row_id, :any, default: nil, doc: "the function for generating the row id"
   attr :row_click, :any, default: nil, doc: "the function for handling phx-click on each row"
-  attr :rows, :list, required: true
+
+  attr :row_item, :any,
+    default: &Function.identity/1,
+    doc: "the function for mapping each row before calling the :col and :action slots"
 
   slot :col, required: true do
     attr :label, :string
@@ -472,8 +476,13 @@ defmodule <%= @web_namespace %>.CoreComponents do
   slot :action, doc: "the slot for showing user actions in the last table column"
 
   def table(assigns) do
+    assigns =
+      with %{rows: %Phoenix.LiveView.LiveStream{}} <- assigns do
+        assign(assigns, row_id: assigns.row_id || fn {id, _item} -> id end)
+      end
+
     ~H"""
-    <div id={@id} class="overflow-y-auto px-4 sm:overflow-visible sm:px-0">
+    <div class="overflow-y-auto px-4 sm:overflow-visible sm:px-0">
       <table class="mt-11 w-[40rem] sm:w-full">
         <thead class="text-left text-[0.8125rem] leading-6 text-zinc-500">
           <tr>
@@ -481,12 +490,12 @@ defmodule <%= @web_namespace %>.CoreComponents do
             <th class="relative p-0 pb-4"><span class="sr-only"><%= if @gettext do %><%%= gettext("Actions") %><% else %>Actions<% end %></span></th>
           </tr>
         </thead>
-        <tbody class="relative divide-y divide-zinc-100 border-t border-zinc-200 text-sm leading-6 text-zinc-700">
-          <tr
-            :for={row <- @rows}
-            id={@row_id && @row_id.(row)}
-            class="group hover:bg-zinc-50"
-          >
+        <tbody
+          id={@id}
+          phx-update={match?(%Phoenix.LiveView.LiveStream{}, @rows) && "stream"}
+          class="relative divide-y divide-zinc-100 border-t border-zinc-200 text-sm leading-6 text-zinc-700"
+        >
+          <tr :for={row <- @rows} id={@row_id && @row_id.(row)} class="group hover:bg-zinc-50">
             <td
               :for={{col, i} <- Enum.with_index(@col)}
               phx-click={@row_click && @row_click.(row)}
@@ -495,7 +504,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
               <div class="block py-4 pr-6">
                 <span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-zinc-50 sm:rounded-l-xl" />
                 <span class={["relative", i == 0 && "font-semibold text-zinc-900"]}>
-                  <%%= render_slot(col, row) %>
+                  <%%= render_slot(col, @row_item.(row)) %>
                 </span>
               </div>
             </td>
@@ -506,7 +515,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
                   :for={action <- @action}
                   class="relative ml-4 font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
                 >
-                  <%%= render_slot(action, row) %>
+                  <%%= render_slot(action, @row_item.(row)) %>
                 </span>
               </div>
             </td>
diff --git a/priv/templates/phx.gen.live/form_component.ex b/priv/templates/phx.gen.live/form_component.ex
index fe1b47a5e..52cf6e85b 100644
--- a/priv/templates/phx.gen.live/form_component.ex
+++ b/priv/templates/phx.gen.live/form_component.ex
@@ -13,6 +13,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
       </.header>
 
       <.simple_form
+        for={@form}
         id="<%= schema.singular %>-form"
         phx-target={@myself}
         phx-change="validate"
@@ -53,11 +54,13 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
 
   defp save_<%= schema.singular %>(socket, :edit, <%= schema.singular %>_params) do
     case <%= inspect context.alias %>.update_<%= schema.singular %>(socket.assigns.<%= schema.singular %>, <%= schema.singular %>_params) do
-      {:ok, _<%= schema.singular %>} ->
+      {:ok, <%= schema.singular %>} ->
+        notify_parent({:saved, <%= schema.singular %>})
+
         {:noreply,
          socket
          |> put_flash(:info, "<%= schema.human_singular %> updated successfully")
-         |> push_navigate(to: socket.assigns.navigate)}
+         |> push_patch(to: socket.assigns.patch)}
 
       {:error, %Ecto.Changeset{} = changeset} ->
         {:noreply, assign_form(socket, changeset)}
@@ -66,11 +69,13 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
 
   defp save_<%= schema.singular %>(socket, :new, <%= schema.singular %>_params) do
     case <%= inspect context.alias %>.create_<%= schema.singular %>(<%= schema.singular %>_params) do
-      {:ok, _<%= schema.singular %>} ->
+      {:ok, <%= schema.singular %>} ->
+        notify_parent({:saved, <%= schema.singular %>})
+
         {:noreply,
          socket
          |> put_flash(:info, "<%= schema.human_singular %> created successfully")
-         |> push_navigate(to: socket.assigns.navigate)}
+         |> push_patch(to: socket.assigns.patch)}
 
       {:error, %Ecto.Changeset{} = changeset} ->
         {:noreply, assign_form(socket, changeset)}
@@ -80,4 +85,6 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
   defp assign_form(socket, %Ecto.Changeset{} = changeset) do
     assign(socket, :form, to_form(changeset))
   end
+
+  defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
 end
diff --git a/priv/templates/phx.gen.live/index.ex b/priv/templates/phx.gen.live/index.ex
index f46c53b77..b4658eeca 100644
--- a/priv/templates/phx.gen.live/index.ex
+++ b/priv/templates/phx.gen.live/index.ex
@@ -6,7 +6,7 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
 
   @impl true
   def mount(_params, _session, socket) do
-    {:ok, assign(socket, :<%= schema.collection %>, list_<%= schema.plural %>())}
+    {:ok, stream(socket, :<%= schema.collection %>, <%= inspect context.alias %>.list_<%= schema.plural %>())}
   end
 
   @impl true
@@ -32,15 +32,16 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
     |> assign(:<%= schema.singular %>, nil)
   end
 
+  @impl true
+  def handle_info({<%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent, {:saved, <%= schema.singular %>}}, socket) do
+    {:noreply, stream_insert(socket, :<%= schema.collection %>, <%= schema.singular %>)}
+  end
+
   @impl true
   def handle_event("delete", %{"id" => id}, socket) do
     <%= schema.singular %> = <%= inspect context.alias %>.get_<%= schema.singular %>!(id)
     {:ok, _} = <%= inspect context.alias %>.delete_<%= schema.singular %>(<%= schema.singular %>)
 
-    {:noreply, assign(socket, :<%= schema.collection %>, list_<%=schema.plural %>())}
-  end
-
-  defp list_<%= schema.plural %> do
-    <%= inspect context.alias %>.list_<%= schema.plural %>()
+    {:noreply, stream_delete(socket, :<%= schema.collection %>, <%= schema.singular %>)}
   end
 end
diff --git a/priv/templates/phx.gen.live/index.html.heex b/priv/templates/phx.gen.live/index.html.heex
index 95f659170..a6daca49d 100644
--- a/priv/templates/phx.gen.live/index.html.heex
+++ b/priv/templates/phx.gen.live/index.html.heex
@@ -9,19 +9,21 @@
 
 <.table
   id="<%= schema.plural %>"
-  rows={@<%= schema.collection %>}
-  row_id={&"<%= schema.plural %>-#{&1.id}"}
-  row_click={&JS.navigate(~p"<%= schema.route_prefix %>/#{&1}")}
+  rows={@streams.<%= schema.collection %>}
+  row_click={fn {_id, <%= schema.singular %>} -> JS.navigate(~p"<%= schema.route_prefix %>/#{<%= schema.singular %>}") end}
 ><%= for {k, _} <- schema.attrs do %>
-  <:col :let={<%= schema.singular %>} label="<%= Phoenix.Naming.humanize(Atom.to_string(k)) %>"><%%= <%= schema.singular %>.<%= k %> %></:col><% end %>
-  <:action :let={<%= schema.singular %>}>
+  <:col :let={{_id, <%= schema.singular %>}} label="<%= Phoenix.Naming.humanize(Atom.to_string(k)) %>"><%%= <%= schema.singular %>.<%= k %> %></:col><% end %>
+  <:action :let={{_id, <%= schema.singular %>}}>
     <div class="sr-only">
       <.link navigate={~p"<%= schema.route_prefix %>/#{<%= schema.singular %>}"}>Show</.link>
     </div>
     <.link patch={~p"<%= schema.route_prefix %>/#{<%= schema.singular %>}/edit"}>Edit</.link>
   </:action>
-  <:action :let={<%= schema.singular %>}>
-    <.link phx-click={JS.push("delete", value: %{id: <%= schema.singular %>.id})} data-confirm="Are you sure?">
+  <:action :let={{id, <%= schema.singular %>}}>
+    <.link
+      phx-click={JS.push("delete", value: %{id: <%= schema.singular %>.id}) |> hide("##{id}")}
+      data-confirm="Are you sure?"
+    >
       Delete
     </.link>
   </:action>
@@ -39,6 +41,6 @@
     title={@page_title}
     action={@live_action}
     <%= schema.singular %>={@<%= schema.singular %>}
-    navigate={~p"<%= schema.route_prefix %>"}
+    patch={~p"<%= schema.route_prefix %>"}
   />
 </.modal>
diff --git a/priv/templates/phx.gen.live/show.html.heex b/priv/templates/phx.gen.live/show.html.heex
index b681fe298..f3628a209 100644
--- a/priv/templates/phx.gen.live/show.html.heex
+++ b/priv/templates/phx.gen.live/show.html.heex
@@ -21,6 +21,6 @@
     title={@page_title}
     action={@live_action}
     <%= schema.singular %>={@<%= schema.singular %>}
-    navigate={~p"<%= schema.route_prefix %>/#{@<%= schema.singular %>}"}
+    patch={~p"<%= schema.route_prefix %>/#{@<%= schema.singular %>}"}
   />
 </.modal>


Test patch:
diff --git a/integration_test/mix.exs b/integration_test/mix.exs
index 2a5c44b40..2e53707aa 100644
--- a/integration_test/mix.exs
+++ b/integration_test/mix.exs
@@ -41,7 +41,7 @@ defmodule Phoenix.Integration.MixProject do
       {:tds, ">= 0.0.0"},
       {:ecto_sqlite3, ">= 0.0.0"},
       {:phoenix_html, "~> 3.3"},
-      {:phoenix_live_view, "~> 0.18.12"},
+      {:phoenix_live_view, "~> 0.18.14"},
       {:floki, ">= 0.30.0"},
       {:phoenix_live_reload, "~> 1.2"},
       {:phoenix_live_dashboard, "~> 0.7.2"},
diff --git a/integration_test/mix.lock b/integration_test/mix.lock
index d7e6c8cd1..ea9dcb21d 100644
--- a/integration_test/mix.lock
+++ b/integration_test/mix.lock
@@ -33,7 +33,7 @@
   "phoenix_html": {:hex, :phoenix_html, "3.3.0", "bf451c71ebdaac8d2f40d3b703435e819ccfbb9ff243140ca3bd10c155f134cc", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "272c5c1533499f0132309936c619186480bafcc2246588f99a69ce85095556ef"},
   "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.2", "97cc4ff2dba1ebe504db72cb45098cb8e91f11160528b980bd282cc45c73b29c", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0e5fdf063c7a3b620c566a30fcf68b7ee02e5e46fe48ee46a6ec3ba382dc05b7"},
   "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.0", "4fe222c0be55fdc3f9c711e24955fc42a7cd9b7a2f5f406f2580a567c335a573", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "bebf0fc2d2113b61cb5968f585367234b7b4c21d963d691de7b4b2dc6cdaae6f"},
-  "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.12", "08356107d3d35eb3bbab4dfc7a7f8a51800eef9779956b573e11e36c5a389732", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dc690050299611a56155c3b4c0d920ec229d00eaff6fe41145700c06acad32c1"},
+  "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.14", "349b491496baa964711bba676f542ba1cf379af28ed1a0b0e74d0448be888609", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4bee150229d0169baae3fb0dfa98fe2cebf46b4db478836b3a3352bdf042e18b"},
   "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
   "phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"},
   "plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"},
diff --git a/priv/templates/phx.gen.live/live_test.exs b/priv/templates/phx.gen.live/live_test.exs
index 6d7e9fe7e..7da5490c7 100644
--- a/priv/templates/phx.gen.live/live_test.exs
+++ b/priv/templates/phx.gen.live/live_test.exs
@@ -35,12 +35,13 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
              |> form("#<%= schema.singular %>-form", <%= schema.singular %>: @invalid_attrs)
              |> render_change() =~ "<%= Mix.Phoenix.Schema.failed_render_change_message(schema) %>"
 
-      {:ok, _, html} =
-        index_live
-        |> form("#<%= schema.singular %>-form", <%= schema.singular %>: @create_attrs)
-        |> render_submit()
-        |> follow_redirect(conn, ~p"<%= schema.route_prefix %>")
+      assert index_live
+             |> form("#<%= schema.singular %>-form", <%= schema.singular %>: @create_attrs)
+             |> render_submit()
+
+      assert_patch(index_live, ~p"<%= schema.route_prefix %>")
 
+      html = render(index_live)
       assert html =~ "<%= schema.human_singular %> created successfully"<%= if schema.string_attr do %>
       assert html =~ "some <%= schema.string_attr %>"<% end %>
     end
@@ -57,12 +58,13 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
              |> form("#<%= schema.singular %>-form", <%= schema.singular %>: @invalid_attrs)
              |> render_change() =~ "<%= Mix.Phoenix.Schema.failed_render_change_message(schema) %>"
 
-      {:ok, _, html} =
-        index_live
-        |> form("#<%= schema.singular %>-form", <%= schema.singular %>: @update_attrs)
-        |> render_submit()
-        |> follow_redirect(conn, ~p"<%= schema.route_prefix %>")
+      assert index_live
+             |> form("#<%= schema.singular %>-form", <%= schema.singular %>: @update_attrs)
+             |> render_submit()
+
+      assert_patch(index_live, ~p"<%= schema.route_prefix %>")
 
+      html = render(index_live)
       assert html =~ "<%= schema.human_singular %> updated successfully"<%= if schema.string_attr do %>
       assert html =~ "some updated <%= schema.string_attr %>"<% end %>
     end
@@ -97,12 +99,13 @@ defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web
              |> form("#<%= schema.singular %>-form", <%= schema.singular %>: @invalid_attrs)
              |> render_change() =~ "<%= Mix.Phoenix.Schema.failed_render_change_message(schema) %>"
 
-      {:ok, _, html} =
-        show_live
-        |> form("#<%= schema.singular %>-form", <%= schema.singular %>: @update_attrs)
-        |> render_submit()
-        |> follow_redirect(conn, ~p"<%= schema.route_prefix %>/#{<%= schema.singular %>}")
+      assert show_live
+             |> form("#<%= schema.singular %>-form", <%= schema.singular %>: @update_attrs)
+             |> render_submit()
+
+      assert_patch(show_live, ~p"<%= schema.route_prefix %>/#{<%= schema.singular %>}")
 
+      html = render(show_live)
       assert html =~ "<%= schema.human_singular %> updated successfully"<%= if schema.string_attr do %>
       assert html =~ "some updated <%= schema.string_attr %>"<% end %>
     end
diff --git a/test/mix/tasks/phx.gen.auth_test.exs b/test/mix/tasks/phx.gen.auth_test.exs
index 3d90b050d..ba8c7c2b3 100644
--- a/test/mix/tasks/phx.gen.auth_test.exs
+++ b/test/mix/tasks/phx.gen.auth_test.exs
@@ -525,7 +525,7 @@ defmodule Mix.Tasks.Phx.Gen.AuthTest do
 
       assert_file("lib/my_app_web/controllers/warehouse/user_confirmation_html/new.html.heex", fn file ->
         assert file =~
-                 ~S|<.simple_form :let={f} as={:user} action={~p"/warehouse/users/confirm"}>|
+                 ~S(<.simple_form :let={f} for={@conn.params["user"]} as={:user} action={~p"/warehouse/users/confirm"}>)
 
         assert file =~
                  ~S|<.link href={~p"/warehouse/users/register"}>Register</.link>|
@@ -599,7 +599,7 @@ defmodule Mix.Tasks.Phx.Gen.AuthTest do
         "lib/my_app_web/controllers/warehouse/user_reset_password_html/new.html.heex",
         fn file ->
           assert file =~
-                   ~S|<.simple_form :let={f} as={:user} action={~p"/warehouse/users/reset_password"}>|
+                   ~S(<.simple_form :let={f} for={@conn.params["user"]} as={:user} action={~p"/warehouse/users/reset_password"}>)
         end
       )
 
@@ -620,7 +620,7 @@ defmodule Mix.Tasks.Phx.Gen.AuthTest do
 
       assert_file("lib/my_app_web/controllers/warehouse/user_session_html/new.html.heex", fn file ->
         assert file =~
-                 ~S|<.simple_form :let={f} for={@conn} action={~p"/warehouse/users/log_in"} as={:user}>|
+                 ~S|<.simple_form :let={f} for={@conn.params["user"]} as={:user} action={~p"/warehouse/users/log_in"}>|
 
         assert file =~
                  ~S|<.link navigate={~p"/warehouse/users/register"}|
diff --git a/test/mix/tasks/phx.gen.live_test.exs b/test/mix/tasks/phx.gen.live_test.exs
index 1f651b536..bd8e73132 100644
--- a/test/mix/tasks/phx.gen.live_test.exs
+++ b/test/mix/tasks/phx.gen.live_test.exs
@@ -339,14 +339,14 @@ defmodule Mix.Tasks.Phx.Gen.LiveTest do
       assert_file "lib/phoenix/tracker/series.ex"
 
       assert_file "lib/phoenix_web/live/series_live/index.ex", fn file ->
-        assert file =~ "assign(socket, :series_collection, list_series())"
+        assert file =~ "stream(socket, :series_collection, Tracker.list_series())"
       end
 
       assert_file "lib/phoenix_web/live/series_live/show.ex"
       assert_file "lib/phoenix_web/live/series_live/form_component.ex"
 
       assert_file "lib/phoenix_web/live/series_live/index.html.heex", fn file ->
-        assert file =~ "@series_collection"
+        assert file =~ "@streams.series_collection"
       end
 
       assert_file "lib/phoenix_web/live/series_live/show.html.heex"