Dynamic layout wrapper component for Phoenix v1.7- and v1.8+ compatibility.
This component automatically detects the Phoenix version and layout configuration to provide seamless integration with parent applications while maintaining backward compatibility.
Usage
Replace direct layout calls with the wrapper:
<%!-- OLD (Phoenix v1.7-) --%>
<%!-- Templates relied on router-level layout config --%>
<%!-- NEW (Phoenix v1.8+) --%>
<PhoenixKitWeb.Components.LayoutWrapper.app_layout flash={@flash}>
<%!-- content --%>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>Configuration
Configure parent layout in config.exs:
config :phoenix_kit,
layout: {MyAppWeb.Layouts, :app}
Summary
Functions
Renders content with the appropriate layout based on configuration and Phoenix version.
Rendering path for auth pages (login, register, reset, confirm, magic link, QR handoff, and the invite-only referral screen).
Functions
Renders content with the appropriate layout based on configuration and Phoenix version.
Automatically handles:
- Phoenix v1.8+ function component layouts
- Phoenix v1.7- legacy layout configuration
- Fallback to PhoenixKit layouts when no parent configured
- Parent layout compatibility with PhoenixKit assigns
Attributes
flash- Flash messages (required)phoenix_kit_current_scope- Current authentication scope (optional)phoenix_kit_current_user- Current user (optional, for backwards compatibility)
Inner Block
inner_block- Content to render within the layout
Attributes
flash(:map) - Defaults to%{}.socket(:any) - Defaults tonil.phoenix_kit_current_scope(:any) - Defaults tonil.phoenix_kit_current_user(:any) - Defaults tonil.page_title(:string) - Defaults tonil.page_subtitle(:string) - Defaults tonil.page_section(:string) - Optional breadcrumb segment rendered between "Admin Panel" andpage_title(e.g. "Users" on a user detail page). Desktop only — collapses along with the rest of the breadcrumb prefix on mobile. Defaults tonil.page_section_path(:string) - Prefixed path (viaPhoenixKit.Utils.Routes.path/1) thepage_sectioncrumb links to. Renders as plain text when omitted. Defaults tonil.page_action(:map) - Optional compact action button rendered right after the breadcrumb title:%{icon: "hero-plus", label: "New template", navigate: path}. Lets a page keep its primary create action without spending an in-content header row.labelbecomes the tooltip/aria-label;icondefaults to hero-plus. Navigation only — for aphx-clickaction (or anything needingphx-target), use the:actionslot instead. ⚠️ Plugin LiveViews rendered through the admin layout can only use this map: the layout threads it as an assign, and a slot cannot travel that way. Defaults tonil.current_path(:string) - Defaults tonil.inner_content(:string) - Defaults tonil.project_title(:string) - Defaults tonil.current_locale(:string) - Defaults tonil.from_layout(:boolean) - Defaults tofalse.pk_pending_invitations(:list) - Defaults to[].module_assigns(:map) - Module-supplied host-consumable assigns. Each key in this map is merged into the assigns set passed to the parent layout (Layouts.app), so a host's custom layout can read e.g.assigns[:phoenix_kit_publishing_translations]from publishing, or any other module-defined key. Plainconn.assignsdon't reach a function-component layout — only declared attrs do — so this single map attribute is how modules thread arbitrary host-consumable data through the boundary without core having to declare each one explicitly. Defaults to%{}.
Slots
action- The same compact action button, for pages whose primary action is not a navigation — aphx-click, aJScommand, anything needingphx-target. The map attribute cannot express those and cannot address a LiveComponent.Takes render priority over the
page_actionattribute. Content is wrapped in the same chip shell, and the contract is one compact control: a multi-action toolbar belongs in the page body, not the breadcrumb bar.⚠️ Only reaches views calling
app_layout/1directly. Plugin LiveViews render throughlayouts/admin.html.heex, which threadspage_actionas an assign — slots do not travel through assigns — so those keep the map.<:action> <button phx-click="new_device" phx-target={@myself} title="Add device"> <.icon name="hero-plus" class="w-4 h-4" /> </button> </:action>inner_block
Rendering path for auth pages (login, register, reset, confirm, magic link, QR handoff, and the invite-only referral screen).
Auth pages do not render inside the host's Layouts.app. That layout is
where a mix phx.new app keeps its logo, framework version and off-site
links, so wrapping sign-in in it put Phoenix Framework branding on the login
page of every kit install — reported by more than one host. Admin already
works this way (render_admin_with_parent/1 never calls the host's :app);
auth was the outlier.
The host's root layout still applies, because PhoenixKitWeb.Integration
never calls put_root_layout. That is where the document shell, assets and
CSRF come from, and it is unaffected.
A host that genuinely wants its own chrome on sign-in opts back in:
config :phoenix_kit, auth_uses_host_layout: true⚠️ Two things a host may notice when it does not: anything wired into the
app layout rather than root — a cookie-consent banner, analytics, a theme
toggle — stops appearing on auth pages only. Root-level wiring is unaffected.
Stock phx.new puts assets and CSRF in root, so conventional hosts see only
the branding disappear, which is the point.
Attributes
flash(:map) (required)phoenix_kit_current_scope(:any) - Defaults tonil.page_title(:string) - Defaults tonil.current_path(:string) - Defaults tonil.pk_pending_invitations(:list) - Defaults to[].
Slots
inner_block(required)