Can I remove `app.html.heex` in favor of just `root.html.heex`?

I’m starting a new Phoenix project with Inertia. It only needs root.html.heex. Therefore, I deleted app.html.heex. Unfortunately, this error shows up upon deletion:

no "app" html template defined for ExampleWeb.Layouts (the module exists but does not define app/1 nor render/2)

Any ideas why this happens? Is there another place where app layout is used?

router.ex:

plug :put_root_layout, html: {ExampleWeb.Layouts, :root}

page_controller.ex:

defmodule ExampleWeb.PageController do
  use ExampleWeb, :controller

  def home(conn, _params) do
    conn |> render_inertia("Home")
  end
end

root.html.heex:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="csrf-token" content={get_csrf_token()} />
    <.inertia_title><%= assigns[:page_title] %></.inertia_title>
    <.inertia_head content={@inertia_head} />
    <link phx-track-static rel="stylesheet" href={~p"/assets/css/app.css"} />
    <script type="module" defer phx-track-static src={~p"/assets/app.js"} />
  </head>
  <body>
    {@inner_content}
  </body>
</html>
1 Like

Found another override.

Inside controller method of web module, from this:

layouts: [html: SubKitWeb.Layouts]

To this:

layouts: [html: {SubKitWeb.Layouts, :root}]
1 Like

No, that would print the root layout twice. Is it not possible to have a single layout in Phoenix by design?

1 Like

Turns out this was due to mistakenly using an older Phoenix version - 1.7 instead of 1.8.

In 1.8, it’s totally possible to avoid app.html.heex.

1 Like

If you’re not using LiveView I’d skip the root layout and use just “layouts”. Root layouts were only introduced because LiveView needed the split between layout parts handled by LV and all the html boilerplate around it.

2 Likes

out of curiosity, where did you find the version mismatch? did you start a new project with the older version?

coz i’m running into the same issue, where i upgraded a 1.7 to 1.8, but it still had to do a put_layout :false to make it stop looking for an app layout

1 Like

Yep, I accidentally had an older version of phx_new, which therefore was generating older Phoenix projects. Had to do mix archive.install hex phx_new to update it and then make a new project.

I noticed the version mismatch while reading the Phoenix source code. The comment mentioned 1.9 while my local version was 1.7 :grinning_face_with_smiling_eyes: (1.8 is the current stable)

Regarding your question, I see these changes in project template from 1.7 to 1.8:

router:

-plug :put_root_layout, {SampleAppWeb.Layouts, :root}
+plug :put_root_layout, html: {SampleAppWeb.Layouts, :root}

web module controller:

-use Phoenix.Controller
-  formats: [:html, :json],
-  layouts: [html: SampleAppWeb.Layouts]
+ use Phoenix.Controller, formats: [:html, :json]

web module liveview

-use Phoenix.LiveView,
-  layout: {SampleAppWeb.Layouts, :app}
+use Phoenix.LiveView

from PhoenixDiff · v1.7.0 to v1.8.3 (scroll to the end to skip all the JS vendor):

1 Like

gotcha thanks, managed to solve it

in my case i have removed the layouts from the use Phoenix.Controller bits, but turns out i also need to remove the namespace args on it. looking it at the source, it is also used to derive the layout module

1 Like