Phoenix: applying different layouts for different routes in LiveView

Today I needed to different layouts for public-facing and authorised pages in a LiveView app.

After an annoying amount of digging in documentation and forums, the following was the most elegant solution I found.

Assume we have these layouts in myapp/lib/myapp_web/components/layouts/:

authenticated.html.heex
public.html.heex

And, also assuming that there is something different in each layout: stuff you can't use unless signed in.

In lib/myapp_web/router.ex modify these authentication routes to add a layout: in the live_session statements:


  scope "/", MyappWeb do
    pipe_through [:browser, :redirect_if_user_is_authenticated]

    live_session :redirect_if_user_is_authenticated,
      on_mount: [{MyappWeb.UserAuth, :redirect_if_user_is_authenticated}],
      layout: {MyappWeb.Layouts, :public} do
      live "/users/register", UserRegistrationLive, :new
      live "/users/log_in", UserLoginLive, :new
      live "/users/reset_password", UserForgotPasswordLive, :new
      live "/users/reset_password/:token", UserResetPasswordLive, :edit
    end

    post "/users/log_in", UserSessionController, :create
  end

  scope "/", ReflectalWeb do
    pipe_through [:browser, :require_authenticated_user]

    live_session :require_authenticated_user,
      on_mount: [{MyappWeb.UserAuth, :ensure_authenticated}],
      layout: {MyappWeb.Layouts, :authenticated} do
      live "/dashboard", DashboardLive.Show, :show
      live "/users/settings", UserSettingsLive, :edit
      live "/users/settings/confirm_email/:token", UserSettingsLive, :confirm_email
    end
  end

The important lines here are:

      layout: {MyappWeb.Layouts, :public} do

and

      layout: {MyappWeb.Layouts, :authenticated} do

Simple!

Discuss...