Skip to content

Allow defining layout in callback function#1808

Merged
Flo0807 merged 6 commits intonaymspace:developfrom
mitchellhenke:runtime-layout
Mar 11, 2026
Merged

Allow defining layout in callback function#1808
Flo0807 merged 6 commits intonaymspace:developfrom
mitchellhenke:runtime-layout

Conversation

@mitchellhenke
Copy link
Copy Markdown
Contributor

Hello!

I was debugging some compile cycles in a Phoenix application, and noticed that backpex was part of the issue.

The demo application has a good example of what I'm running into. If you run mix xref graph --format cycles --label compile-connected, it reports that there is a compile cycle:

demo/ $ mix xref graph --format cycles --label compile-connected
1 cycles found. Showing them in decreasing size:

Cycle of length 16 (9 compile, 1 export):

    lib/demo_web/components/core_components.ex
    lib/demo_web/components/layouts.ex (export)
    lib/demo_web/controllers/redirect_controller.ex
    lib/demo_web/endpoint.ex
    lib/demo_web/item_actions/user_soft_delete.ex
    lib/demo_web/live/address_live.ex (compile)
    lib/demo_web/live/category_live.ex (compile)
    lib/demo_web/live/film_review_live.ex (compile)
    lib/demo_web/live/invoice_live.ex (compile)
    lib/demo_web/live/post_live.ex (compile)
    lib/demo_web/live/product_live.ex (compile)
    lib/demo_web/live/short_link_live.ex (compile)
    lib/demo_web/live/tag_live.ex (compile)
    lib/demo_web/live/user_live.ex (compile)
    lib/demo_web/resource_actions/upload.ex
    lib/demo_web/router.ex

This results in a change in any of the files recompiling all of the files, which usually means slower compile times during development in apps that use Backpex.

I'm not deeply familiar with Elixir compiler functionality, but my understanding is the reason for it is the DemoWeb.Layouts becomes a compile dependency of the LiveViews when specified in use Backpex.LiveResource. DemoWeb.Layouts includes use DemoWeb, :html which ends up pulling in the router, etc.

To continue the above example using the demo app, if you apply this patch to replace the layout option with the callback, the demo app no longer has a compile cycle:

demo/ $ mix xref graph --format cycles --label compile-connected
No cycles found

I'm proposing allowing specifying the layout as a callback function to avoid the specific compile dependency between layouts and the LiveView. To avoid a breaking change, I've not removed the current option. I also haven't tried updating any guides, documentation, or similar, but am happy to if you're open to this change.

mitchellhenke and others added 2 commits February 11, 2026 13:39
- Default layout/1 returns config(:layout) instead of nil for backwards compatibility
- Simplify layout.ex to a single code path through the callback (no if/else branching)
- Fix callback typespec to match actual return type (layout spec, not rendered HTML)
@Flo0807
Copy link
Copy Markdown
Collaborator

Flo0807 commented Mar 6, 2026

Hey, thanks for your contribution!

@Flo0807 Flo0807 added the enhancement Changes that are not breaking label Mar 6, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new layout/1 callback to Backpex.LiveResource so consumers can define the LiveView layout via a function (instead of a compile-time layout: option), aiming to reduce compile-time dependencies (and related compile cycles).

Changes:

  • Make the layout option in use Backpex.LiveResource optional and allow a layout/1 callback.
  • Add c:layout/1 behavior + a default implementation returning config(:layout) and mark it overridable.
  • Update Backpex.HTML.Layout.layout/1 to call assigns.live_resource.layout(assigns) instead of reading config(:layout) directly.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
lib/backpex/live_resource.ex Adds layout/1 callback + default implementation; makes layout option optional.
lib/backpex/html/layout.ex Switches layout resolution to call the new layout/1 callback.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/backpex/live_resource.ex
Comment thread lib/backpex/live_resource.ex
Comment thread lib/backpex/live_resource.ex Outdated
Comment thread lib/backpex/html/layout.ex
The default layout/1 callback now raises an ArgumentError with an
actionable message when neither the layout option nor a layout/1
callback override is provided, instead of letting nil propagate
to a CaseClauseError at render time.
@Flo0807 Flo0807 self-assigned this Mar 6, 2026
@Flo0807 Flo0807 requested a review from pehbehbeh March 6, 2026 12:54
@mitchellhenke
Copy link
Copy Markdown
Contributor Author

Hey, thanks for your contribution!

Of course, thanks for the very helpful library! Happy to help with getting this merged, just let me know 🙂

Copy link
Copy Markdown
Member

@pehbehbeh pehbehbeh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Maybe even soft deprecate the old way via NimbleOptions (see :deprecated option)?

@Flo0807 Flo0807 added this pull request to the merge queue Mar 11, 2026
@Flo0807 Flo0807 added feature New feature and removed enhancement Changes that are not breaking labels Mar 11, 2026
Merged via the queue into naymspace:develop with commit f484499 Mar 11, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants