Components Table

Table

Tables are a great way to clearly display a collections of data grouped into columns and rows.
Dynamic table
Name Age
John Smith 30
Beth Springs 25
Peter Knowles 40
Sarah Hill 35
          <.table
  id="posts"
  row_id={fn post -> "row_#{post.id}" end}
  row_click={fn post -> JS.navigate(~p"/components/table#row_#{post.id}") end}
  rows={[
    %{
      id: 1,
      name: "John Smith",
      age: 30
    },
    %{
      id: 2,
      name: "Beth Springs",
      age: 25
    },
    %{
      id: 3,
      name: "Peter Knowles",
      age: 40
    },
    %{
      id: 4,
      name: "Sarah Hill",
      age: 35
    }
  ]}
>
  <:col :let={post} label="Name"><%= post.name %></:col>
  <:col :let={post} label="Age"><%= post.age %></:col>
  <:col class="w-64">
    <div class="flex gap-2 items-center justify-end">
      <.button label="Show" />
      <.button label="Delete" color="danger" />
    </div>
  </:col>
</.table>

        
With streams
Basic table
Name Title Email
John Smith Engineer john.smith@example.com
Beth Springs Developer beth.springs@example.com
Peter Knowles Programmer Peter.knowles@example.com
Sarah Hill Marketer sarah.hill@example.com
          <.table>
  <.tr>
    <.th>Name</.th>
    <.th>Title</.th>
    <.th>Email</.th>
  </.tr>

  <.tr>
    <.td>
      John Smith
    </.td>
    <.td>Engineer</.td>
    <.td>john.smith@example.com</.td>
  </.tr>

  <.tr>
    <.td>
      Beth Springs
    </.td>
    <.td>Developer</.td>
    <.td>beth.springs@example.com</.td>
  </.tr>

  <.tr>
    <.td>
      Peter Knowles
    </.td>
    <.td>Programmer</.td>
    <.td>Peter.knowles@example.com</.td>
  </.tr>

  <.tr>
    <.td>
      Sarah Hill
    </.td>
    <.td>Marketer</.td>
    <.td>sarah.hill@example.com</.td>
  </.tr>
</.table>

        
Multi-lined table with avatars and badges
Name Title Phone Status
https://res.cloudinary.com/wickedsites/image/upload/v1636595188/dummy_data/avatar_2_jhs6ww.png
John Smith
john.smith@example.com
Engineer +1 0432 677 943 Active Edit
https://res.cloudinary.com/wickedsites/image/upload/v1636595188/dummy_data/avatar_1_lc8plf.png
Beth Springs
beth.springs@example.com
Developer +1 0465 899 443 Pending Edit
https://res.cloudinary.com/wickedsites/image/upload/v1636595189/dummy_data/avatar_14_rkiyfa.png
Peter Knowles
peter.knowles@example.com
Programmer +1 0472 344 565 Cancelled Edit
https://res.cloudinary.com/wickedsites/image/upload/v1604268092/unnamed_sagz0l.jpg
Sarah Hill
sarah.hill@example.com
Marketer +1 0429 996 220 Deleted Edit
          <.table>
  <.tr>
    <.th>Name</.th>
    <.th>Title</.th>
    <.th>Phone</.th>
    <.th>Status</.th>
    <.th></.th>
  </.tr>

  <.tr>
    <.td>
      <.user_inner_td
        avatar_assigns={%{src: "https://res.cloudinary.com/wickedsites/image/upload/v1636595188/dummy_data/avatar_2_jhs6ww.png"}}
        label="John Smith"
        sub_label="john.smith@example.com"
      />
    </.td>
    <.td>Engineer</.td>
    <.td class="whitespace-nowrap">+1 0432 677 943</.td>
    <.td>
      <.badge color="success" label="Active" />
    </.td>
    <.td>
      <.a to="/" label="Edit" class="text-primary-600 dark:text-primary-400" />
    </.td>
  </.tr>

  <.tr>
    <.td>
      <.user_inner_td
        avatar_assigns={%{src: "https://res.cloudinary.com/wickedsites/image/upload/v1636595188/dummy_data/avatar_1_lc8plf.png"}}
        label="Beth Springs"
        sub_label="beth.springs@example.com"
      />
    </.td>
    <.td>Developer</.td>
    <.td class="whitespace-nowrap">+1 0465 899 443</.td>
    <.td>
      <.badge color="warning" label="Pending" />
    </.td>
    <.td>
      <.a to="/" label="Edit" class="text-primary-600 dark:text-primary-400" />
    </.td>
  </.tr>

  <.tr>
    <.td>
      <.user_inner_td
        avatar_assigns={%{src: "https://res.cloudinary.com/wickedsites/image/upload/v1636595189/dummy_data/avatar_14_rkiyfa.png"}}
        label="Peter Knowles"
        sub_label="peter.knowles@example.com"
      />
    </.td>
    <.td>Programmer</.td>
    <.td class="whitespace-nowrap">+1 0472 344 565</.td>
    <.td>
      <.badge color="gray" label="Cancelled" />
    </.td>
    <.td>
      <.a to="/" label="Edit" class="text-primary-600 dark:text-primary-400" />
    </.td>
  </.tr>

  <.tr>
    <.td>
      <.user_inner_td
        avatar_assigns={%{src: "https://res.cloudinary.com/wickedsites/image/upload/v1604268092/unnamed_sagz0l.jpg"}}
        label="Sarah Hill"
        sub_label="sarah.hill@example.com"
      />
    </.td>
    <.td>Marketer</.td>
    <.td class="whitespace-nowrap">+1 0429 996 220</.td>
    <.td>
      <.badge color="danger" label="Deleted" />
    </.td>

    <.td>
      <.a to="/" label="Edit" class="text-primary-600 dark:text-primary-400" />
    </.td>
  </.tr>
</.table>

        
Properties
            # <.table>
  attr :id, :string
  attr :class, :string, default: "", doc: "CSS class"
  attr :rows, :list, default: [], doc: "the list of rows to render"
  attr :row_id, :any, default: nil, doc: "the function for generating the row id"
  attr :row_click, :any, default: nil, doc: "the function for handling phx-click on each row"

  attr :row_item, :any,
    default: &Function.identity/1,
    doc: "the function for mapping each row before calling the :col slot"

  slot :col do
    attr :label, :string
    attr :class, :string
    attr :row_class, :string
  end

  attr :rest, :global, include: ~w(colspan rowspan)

  # <.th>
  attr(:class, :string, default: "", doc: "CSS class")
  attr(:rest, :global, include: ~w(colspan rowspan))
  slot(:inner_block, required: false)

  # <.tr>
  attr(:class, :string, default: "", doc: "CSS class")
  attr(:rest, :global)
  slot(:inner_block, required: false)

  # <.td>
  attr(:class, :string, default: "", doc: "CSS class")
  attr(:rest, :global, include: ~w(colspan headers rowspan))
  slot(:inner_block, required: false)

  # <.user_inner_td>
  attr(:class, :string, default: "", doc: "CSS class")
  attr(:label, :string, default: nil, doc: "Adds a label your user, e.g name")
  attr(:sub_label, :string, default: nil, doc: "Adds a sub-label your to your user, e.g title")
  attr(:rest, :global)

  attr(:avatar_assigns, :map, default: nil, doc: "if using an avatar, this map will be passed to the avatar component as props")