PhoenixKitWeb.Components.LayoutWrapper (phoenix_kit v2.0.1)

Copy Markdown View Source

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

app_layout(assigns)

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 to nil.
  • phoenix_kit_current_scope (:any) - Defaults to nil.
  • phoenix_kit_current_user (:any) - Defaults to nil.
  • page_title (:string) - Defaults to nil.
  • page_subtitle (:string) - Defaults to nil.
  • page_section (:string) - Optional breadcrumb segment rendered between "Admin Panel" and page_title (e.g. "Users" on a user detail page). Desktop only — collapses along with the rest of the breadcrumb prefix on mobile. Defaults to nil.
  • page_section_path (:string) - Prefixed path (via PhoenixKit.Utils.Routes.path/1) the page_section crumb links to. Renders as plain text when omitted. Defaults to nil.
  • 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. label becomes the tooltip/aria-label; icon defaults to hero-plus. Navigation only — for a phx-click action (or anything needing phx-target), use the :action slot 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 to nil.
  • current_path (:string) - Defaults to nil.
  • inner_content (:string) - Defaults to nil.
  • project_title (:string) - Defaults to nil.
  • current_locale (:string) - Defaults to nil.
  • from_layout (:boolean) - Defaults to false.
  • 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. Plain conn.assigns don'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 — a phx-click, a JS command, anything needing phx-target. The map attribute cannot express those and cannot address a LiveComponent.

    Takes render priority over the page_action attribute. 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/1 directly. Plugin LiveViews render through layouts/admin.html.heex, which threads page_action as 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

auth_layout(assigns)

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 to nil.
  • page_title (:string) - Defaults to nil.
  • current_path (:string) - Defaults to nil.
  • pk_pending_invitations (:list) - Defaults to [].

Slots

  • inner_block (required)

build_locale_url(current_path, base_code)

generate_language_switch_url(current_path, new_locale)

get_language_flag(code)