Set Different root.html or Extend Same Base Template For Different Views

I have a fairly straightforward problem, but I’m having a tricky time deciphering an answer from the documentation.

My applications has three parts, a front page, various dashboard interfaces/lobbies, and a main game page.

I’d like to have a common template for all of the dashboard pages. I thought that maybe I could use a different root.html for those pages, but the documentation suggests that this is some kind of magic value, such that I can only one root.html. So, I’d like to keep the universal root, but still have dashboard pages inherit a common template.

What is that best way for different views to share a common template other than the root? Does Phoenix provide something equivalent to Django’s {% extends 'base.html' %}?

Thanks!

1 Like

I think you’re looking for this:

pipeline :dashboard do
  plug :put_layout, {MyApp.LayoutView, "dashboard.html"}
end

See more details here: Multiple Layouts with Phoenix - ElixirCasts

1 Like

Thanks, that’s a good starting point. But, it looks like only for non-Live views, and it doesn’t use the root.html, but rather a base template which contains all of the headers in it, meaning that those will all have to be updated independently for site-wide changes. I’d like something structured like this:

  • root
    • dashboard_base
      • user_settings

I have found references online to render_existing, but this appears to have been removed in a previous version. Was it replaced by anything?

1 Like

OK, so it seems that render_layout is more appropriate here.

Alternatively, you could use a component (I’m using Surface UI). My app does this:

<App conn={@conn} active_link={...}>
  <!-- content -->
</App>
2 Likes

As for render_existing, you can always look in the doc for the replacement in this case:

render_existing/3

1 Like

I found this, which has the right idea, although appears to be slightly outdated and isn’t working as I expected: Nested Layouts With Phoenix « Plataformatec Blog

1 Like

If you are on latest phoenix and you can use heex it would be better to use function components.

<.header conn={@conn}></.header>
<div class="d-flex">
  <aside class="flex-shrink-0">
    <.side_bar conn={@conn}></.side_bar>
  </aside>
  <main class="flex-grow-1">
    <!-- main content goes here -->
  </main>
</div>

I followed the Nested Layouts blog post earlier - migrated to function components.

3 Likes

You can set the root layout via put_root_layout or root_layout on the live_session:

When working with LiveViews, there are usually three layouts to be
considered:

  • the root layout - this is a layout used by both LiveView and
    regular views. This layout typically contains the <html>
    definition alongside the head and body tags. Any content defined
    in the root layout will remain the same, even as you live navigate
    across LiveViews. All LiveViews defined at the router must have
    a root layout. The root layout is typically declared on the
    router with put_root_layout and defined as “root.html.heex”
    in your MyAppWeb.LayoutView. It may also be given via the
    :root_layout option to a live_session macro in the router.
1 Like

render_existing/3 is deprecated, use function_exported?/3 instead :slight_smile:

1 Like

To follow up with this, I ended up using some of the ideas outlined here, which let me use the existing universal root along with nested templates where I want them, as shared components. Worked out fine.

Thanks for everybody’s input!

2 Likes

This is my final post about this, but I’ve come to the conclusion that my overall strategy was wrong, and it’s better to just forget the Django paradigm of templates-in-templates-in-templates entirely and instead think in terms of reusable components. Ex - rather than having a ‘dashboard’ template, instead have ‘header’, ‘sidebar’ and ‘footer’ components which you drop in a minimal scaffold. This prevents headaches with live_component and needing to access @socket in places you might not anticipate.

3 Likes