This guide covers the practical migration path from the default phx.new UI
layer to PUI.
What to replace
- Generated
core_components.exhelpers such as custom flash, button, input, and icon wrappers phx.newlayout markup like the topbar, flash area, and generated shell structure- App-local sidebar or submenu hooks that can move into bundled PUI hooks
Core migration steps
1. Import PUI in shared web entrypoints
def live_view do
quote do
use Phoenix.LiveView
use PUI
end
end
2. Replace generated flash and loading UI
<PUI.Loading.topbar />
<PUI.Flash.flash_group flash={@flash} />
Use live={true} only inside LiveView-rendered trees. Shared layouts that also
render controller templates should use the default non-live flash group.
3. Move app shell markup to PUI.Layout
<.app_layout id="app-shell">
<:sidebar>
<.sidebar>
...
</.sidebar>
</:sidebar>
<:header>
<.content_header shell_id="app-shell" breadcrumb_current="Dashboard" />
</:header>
...
</.app_layout>
4. Remove app-local submenu hooks
If you were relying on a colocated sidebar collapse hook, move to
sidebar_menu_item/1 and the bundled PUI.Sidebar hook by registering
PUIHooks in your LiveSocket.
Example target
Open the full-page demo to see how the generated layout, topbar, sidebar, and local submenu hook can collapse into reusable PUI primitives:
Notes
- Migrate incrementally and compile after each phase.
- Replace generated auth and layout UI with plain HEEx plus PUI primitives rather than re-creating
phx.newhelper wrappers. - Keep app-specific copy and information architecture local; use PUI for behavior and surfaces.