FullstackPhoenix

Purpose-Built Layouts for Phoenix SaaS Apps

phoenixlayoutsliveviewui

You ship the first marketing page for your SaaS and immediately notice the problem: the public landing page now has the signed-in app nav with broken links. The login page has a "log out" button. Everything is technically rendering, but the chrome is wrong everywhere except the dashboard. The default Phoenix single root layout was never going to handle this gracefully.

This feature splits the layout into three purpose-built modules so each kind of page gets exactly the chrome it should have. The shared building blocks — logo, avatar, theme toggle — are extracted and reused, so the visual identity stays consistent even though the structure differs.

One layout per audience, not one layout for everyone

A SaaS has at least three different reading contexts: signed-in product pages, authentication flows, and public marketing or docs pages. They have different navs, different footers, and different conversion goals. Trying to handle all three in a single root layout forces conditional rendering that gets brittle quickly. Splitting them is a one-time cost that keeps every future page simple.

What This Feature Adds

  • lib/my_app_web/components/layouts/app.ex — main signed-in layout with full navigation, theme toggle, avatar, and flash
  • lib/my_app_web/components/layouts/session.ex — minimal layout for phx.gen.auth login, registration, and reset pages (no app nav)
  • lib/my_app_web/components/layouts/public.ex — public-facing layout for marketing, docs, and other unauthenticated pages
  • A refactored lib/my_app_web/components/layouts.ex that delegates to the three modules
  • Updated phx.gen.auth LiveViews so they use Layouts.Session.page instead of the default layout
  • Shared logo and avatar components extracted so the three layouts stay visually consistent

How It Fits Into Your Phoenix Application

Once installed, each LiveView and controller declares the layout it wants explicitly:

<Layouts.App.page flash={@flash} current_scope={@current_scope}>
<div class="space-y-6">
<!-- product content -->
</div>
</Layouts.App.page>

The Session layout is automatically used by the generated auth LiveViews; the Public layout is intended for marketing pages, the /waitlist page, and any other unauthenticated content. The theme toggle continues to work across all three layouts because it is implemented in shared components.

Installation Notes

  • No hard feature dependencies, but layouts is itself a dependency for admin, notifications, and (through the locale picker) several other features. Install it early in the order.
  • The feature rewrites generated auth LiveViews to use the session layout. If you've already customised those LiveViews, expect to merge the changes.
  • The Layouts.Public.page component is intentionally minimal. Customise it for your landing page — fonts, hero typography, footer.

Build This With Live SaaS Kit

Install saas_kit, then mix saaskit.feature.install layouts to stop forcing one root layout to do three different jobs.