How to Build Nested Layouts

Copy Markdown View Source

Use this guide when a screen should render multiple related resources in one layout tree.

For stored and published layouts, see How to Work with SDUI Layouts.

Start with Builder.resource/2

Prefer AshSDUI.Layout.Builder.resource/2 so the node can derive its component and for_resource metadata from the UI module.

alias AshSDUI.Layout.Builder

root =
  Builder.resource(MyApp.UI.PostUI,
    id: "post-show-card-#{post.id}",
    subject_id: post.id
  )

This is the preferred entrypoint for authoring nested layout trees.

Add child resources by region

Attach related resources as child nodes in named regions.

Builder.resource(MyApp.UI.PostUI,
  id: "post-show-card-#{post.id}",
  subject_id: post.id,
  children: [
    Builder.resource(MyApp.UI.UserUI,
      id: "post-show-author-#{post.id}",
      region: :author,
      subject_id: post.author_id
    )
  ]
)

This is a good fit for structures such as:

  • author cards inside posts
  • sidebar records beside a main record
  • nested detail views

Use Builder.resources/3 when a related collection should become one child node per record.

comment_nodes =
  Builder.resources(MyApp.UI.CommentUI, comments,
    id_prefix: "post-show-comment",
    region: :comments
  )

root =
  Builder.resource(MyApp.UI.PostUI,
    subject_id: post.id,
    children: comment_nodes
  )

This is the preferred path for repeated nested regions such as comments, activity items, or related records.

Compose multiple nested regions

You can mix singular and repeated children in one tree.

Builder.resource(MyApp.UI.PostUI,
  subject_id: post.id,
  children: [
    Builder.resource(MyApp.UI.UserUI,
      region: :author,
      subject_id: post.author_id
    )
    | Builder.resources(MyApp.UI.CommentUI, comments,
        id_prefix: "comment",
        region: :comments
      )
  ]
)

This pattern is already proven in the demo layouts for posts, authors, and comments.

Assign the nested layout at runtime

Use AshSDUI.LiveScreen.assign_layout/3 when the nested tree is built from current assigns or records inside a LiveView.

defp assign_post_state(socket, post, comments) do
  {layout_name, root} = MyApp.UI.Layouts.PostShowLayout.build(post, comments)

  AshSDUI.LiveScreen.assign_layout(socket, layout_name, root)
end

Use this path when the tree should be rebuilt on the fly instead of stored.