PhiaUi.Components.Skeleton (phia_ui v0.1.17)

Copy Markdown View Source

Animated loading placeholder components (shimmer / skeleton screens).

Skeleton screens replace content areas while data is loading, preventing layout shift and providing a smoother perceived loading experience than a spinner. All components use Tailwind v4 animate-pulse — pure HEEx, no JavaScript required.

Sub-components

ComponentPurpose
skeleton/1Base rectangle or circle placeholder block
skeleton_text/1Stack of text-line placeholders simulating a paragraph
skeleton_avatar/1Circular placeholder for avatar images
skeleton_card/1Composite card-shaped placeholder (image + text lines)

Basic shapes

<%!-- Rectangle placeholder (for an image, header, or button) --%>
<.skeleton class="h-4 w-full" />

<%!-- Circle placeholder (for an avatar) --%>
<.skeleton shape={:circle} width="40px" height="40px" />

Dashboard card loading skeletons

Replace stat cards with skeletons while data loads:

<%= if @loading do %>
  <div class="grid grid-cols-3 gap-4">
    <%= for _ <- 1..3 do %>
      <.skeleton_card />
    <% end %>
  </div>
<% else %>
  <.metric_grid>
    <%= for stat <- @stats do %>
      <.stat_card title={stat.title} value={stat.value} />
    <% end %>
  </.metric_grid>
<% end %>

List item skeletons

Use skeleton_avatar/1 with skeleton_text/1 to match a user list:

<%= if @loading do %>
  <div class="space-y-4">
    <%= for _ <- 1..5 do %>
      <div class="flex items-center gap-4">
        <.skeleton_avatar size="10" />
        <div class="flex-1">
          <.skeleton_text lines={2} />
        </div>
      </div>
    <% end %>
  </div>
<% else %>
  <%= for user <- @users do %>
    <.user_row user={user} />
  <% end %>
<% end %>

Article / blog post skeleton

<div class="space-y-4 max-w-2xl">
  <%!-- Hero image area --%>
  <.skeleton class="h-56 w-full" />
  <%!-- Title --%>
  <.skeleton class="h-8 w-3/4" />
  <%!-- Author line --%>
  <div class="flex items-center gap-2">
    <.skeleton_avatar size="8" />
    <.skeleton class="h-4 w-32" />
  </div>
  <%!-- Body paragraphs --%>
  <.skeleton_text lines={5} />
  <.skeleton_text lines={4} />
</div>

Table skeleton

Match the column widths of your real table:

<table>
  <thead>...</thead>
  <tbody>
    <%= if @loading do %>
      <%= for _ <- 1..8 do %>
        <tr>
          <td class="p-4"><.skeleton class="h-4 w-8" /></td>
          <td class="p-4"><.skeleton class="h-4 w-40" /></td>
          <td class="p-4"><.skeleton class="h-4 w-24" /></td>
          <td class="p-4"><.skeleton class="h-4 w-20" /></td>
        </tr>
      <% end %>
    <% else %>
      ...real rows...
    <% end %>
  </tbody>
</table>

Summary

Functions

Renders a single animated skeleton placeholder block.

Renders a circular skeleton placeholder for avatar images.

Renders a composite card-shaped skeleton with a header image area and text lines below it.

Renders a form-shaped skeleton with label + input pairs.

Renders a list of skeleton rows simulating a user list, contact list, or feed.

Renders a profile-page-shaped skeleton.

Renders skeleton rows for a table body.

Renders a stack of skeleton lines simulating a paragraph of text.

Functions

skeleton(assigns)

Renders a single animated skeleton placeholder block.

Use :rectangle (default) for any block-level placeholder: image areas, text lines, buttons, or card borders. Use :circle for avatar placeholders.

Size the skeleton with Tailwind classes via :class, or with explicit pixel/rem values via :width and :height. Both can be used together.

Examples

<%!-- Full-width text line --%>
<.skeleton class="h-4 w-full" />

<%!-- Fixed-size image placeholder --%>
<.skeleton class="h-40 w-full" />

<%!-- Circle with explicit pixel size --%>
<.skeleton shape={:circle} width="48px" height="48px" />

Attributes

  • shape (:atom) - Shape of the placeholder. :rectangle (default) for blocks and text lines; :circle for avatars. Defaults to :rectangle. Must be one of :rectangle, or :circle.
  • width (:string) - CSS width applied as an inline style (e.g. "200px", "50%"). When nil, width is controlled by the class attribute. Defaults to nil.
  • height (:string) - CSS height applied as an inline style (e.g. "48px", "2rem"). When nil, height is controlled by the class attribute. Defaults to nil.
  • class (:string) - Additional CSS classes merged via cn/1. Use Tailwind h-N w-N to size the skeleton. Defaults to nil.
  • Global attributes are accepted. HTML attributes forwarded to the root <div> element.

skeleton_avatar(assigns)

Renders a circular skeleton placeholder for avatar images.

The :size attribute maps directly to Tailwind's numeric sizing scale:

sizeoutput classespx size
"6"w-6 h-624 px
"8"w-8 h-832 px
"10"w-10 h-1040 px
"14"w-14 h-1456 px

Example

<%!-- Default 40px avatar skeleton --%>
<.skeleton_avatar />

<%!-- 56px skeleton matching a "lg" avatar --%>
<.skeleton_avatar size="14" />

Attributes

  • size (:string) - Tailwind size number that maps to both w-N and h-N. For example, "10" produces w-10 h-10 (40px). Common values: "6" (24px), "8" (32px), "10" (40px), "14" (56px). Defaults to "10".
  • class (:string) - Additional CSS classes merged with the size-derived classes. Defaults to nil.

skeleton_card(assigns)

Renders a composite card-shaped skeleton with a header image area and text lines below it.

Useful as a drop-in replacement for product cards, blog post cards, or dashboard stat cards while their content is loading.

Example

<div class="grid grid-cols-3 gap-4">
  <%= for _ <- 1..3 do %>
    <.skeleton_card />
  <% end %>
</div>

Attributes

  • class (:string) - Additional CSS classes applied to the card wrapper <div>. Defaults to nil.

skeleton_form(assigns)

Renders a form-shaped skeleton with label + input pairs.

Useful as a placeholder while a form's initial values are loading (e.g. an edit form waiting for a record to be fetched).

Example

<%= if @loading do %>
  <.skeleton_form fields={6} />
<% else %>
  <.my_form changeset={@changeset} />
<% end %>

Attributes

  • fields (:integer) - Number of label + input field pairs to render. Defaults to 4.
  • show_submit (:boolean) - When true, renders a button skeleton at the bottom. Defaults to true.
  • class (:string) - Additional CSS classes. Defaults to nil.

skeleton_list(assigns)

Renders a list of skeleton rows simulating a user list, contact list, or feed.

Each row has an optional avatar circle on the left and two text lines (title + subtitle) on the right.

Example

<%= if @loading do %>
  <.skeleton_list items={8} />
<% else %>
  <%= for user <- @users do %>
    <.user_row user={user} />
  <% end %>
<% end %>

Attributes

  • items (:integer) - Number of list item rows to render. Defaults to 5.
  • show_avatar (:boolean) - When true, each row has a circular avatar placeholder on the left. Defaults to true.
  • class (:string) - Additional CSS classes for the wrapper. Defaults to nil.

skeleton_profile(assigns)

Renders a profile-page-shaped skeleton.

Simulates the typical layout of a user profile: large avatar, name, bio, and a row of stats (posts, followers, following).

Example

<%= if @loading do %>
  <.skeleton_profile />
<% else %>
  <.user_profile user={@user} />
<% end %>

Attributes

  • class (:string) - Additional CSS classes. Defaults to nil.

skeleton_table_row(assigns)

Renders skeleton rows for a table body.

Use inside a <tbody> while table data is loading. The :columns attr controls how many cells to render per row.

Example

<tbody>
  <%= if @loading do %>
    <.skeleton_table_row columns={5} rows={8} />
  <% else %>
    <%= for row <- @rows do %>
      <tr>...</tr>
    <% end %>
  <% end %>
</tbody>

Attributes

  • columns (:integer) - Number of cell placeholders per row. Defaults to 4.
  • rows (:integer) - Number of rows to render. Defaults to 5.
  • class (:string) - Additional CSS classes for the wrapper. Defaults to nil.

skeleton_text(assigns)

Renders a stack of skeleton lines simulating a paragraph of text.

The last line is always rendered at 75% width (w-3/4) to mimic the natural way text rarely fills the entire last line of a paragraph.

Example

<%!-- Simulates 4 lines of body text --%>
<.skeleton_text lines={4} />

Attributes

  • lines (:integer) - Number of text lines to render. Default: 3. The last line is rendered at w-3/4 to simulate natural text flow. Defaults to 3.
  • class (:string) - Additional CSS classes applied to the wrapper <div>. Defaults to nil.