---
title: Skeleton
description: Placeholder shapes that hold the layout until content loads.
category: Status
status: beta
since: 0.3.4
tags: [skeleton, loading, placeholder]
syncStatus: reviewed
verified: true
---

## Usage

### Text and Avatar
`lines` sets the line count; `variant="circle"` with equal `width` and `height` draws a true circle.
:::TuffDemoWrapper{demo="SkeletonSkeletonDemo" code-lang="vue"}
---
code: |
  <template>
    <TxSkeleton :loading="true" :lines="3" />
    <TxSkeleton variant="circle" :width="40" :height="40" />
  </template>
---
:::

### Card Placeholder
:::TuffDemoWrapper{demo="SkeletonCardPlaceholderDemo" code-lang="vue"}
---
code: |
  <template>
    <TxCard>
      <TxSkeleton :loading="true" :lines="2" />
    </TxCard>
  </template>
---
:::

### Dashboard Data Operations
Side summaries beside the table keep their structure with `TxSkeleton` and `TxLayoutSkeleton`.
:::TuffDemoWrapper{demo="ComponentsDataOperationsDemo" code-lang="vue"}
---
code: |
  <template>
    <TxSkeleton :loading="true" :lines="3" height="10px" />
    <TxLayoutSkeleton />
  </template>
---
:::

### Best Practices

- Match width, height, line count, and radius to the final content so loading doesn't create a second layout.
- Use `TxCardSkeleton` or `TxListItemSkeleton` only when their fixed structure matches the final UI.
- Hand off to real content with `loading=false` rather than keeping skeleton and content in separate branches.
- If the final structure is unknown, use an empty or loading state with copy instead.
- For tables, use [TxDataTable](./data-table.en.mdc) with `loadingVariant="skeleton"` instead of composing skeleton rows.

## API Reference

### Props
::TuffPropsTable
---
rows:
  - name: loading
    type: 'boolean'
    default: 'true'
    description: 'Shows the skeleton; `false` renders the default slot.'
  - name: lines
    type: 'number'
    default: '1'
    description: 'Number of placeholder items; at least 1.'
  - name: variant
    type: "'text' | 'rect' | 'circle'"
    default: 'text'
    description: 'Shape of each placeholder item.'
  - name: width
    type: 'string | number'
    default: '100%'
    description: 'Width; numbers are px.'
  - name: height
    type: 'string | number'
    default: '12'
    description: 'Height; numbers are px.'
  - name: radius
    type: 'string | number'
    default: '8'
    description: 'Corner radius of non-circle items; numbers are px.'
  - name: gap
    type: 'string | number'
    default: '10'
    description: 'Gap between lines; numbers are px.'
---
::

### Slots

| Component | Slot | Props | Description |
|-----------|------|-------|-------------|
| `TxSkeleton` | `default` | - | Real content, rendered when `loading=false`. |
| `TxCardSkeleton` | - | - | Fixed card placeholder; no slots. |
| `TxListItemSkeleton` | - | - | Fixed list-row placeholder; no slots. |

### Preset Components

| Export | Purpose |
|--------|---------|
| `TxSkeleton` / `Skeleton` | Configurable text, rectangle, or circle placeholder. |
| `TxCardSkeleton` / `CardSkeleton` | Card or feed placeholder: icon, title, badge, and description. |
| `TxListItemSkeleton` / `ListItemSkeleton` | List-row placeholder: icon, name, meta, and trailing badge. |
| `TxRowSkeleton` / `RowSkeleton` | Settings-row placeholder: optional icon, description, and trailing control. |

### TxRowSkeleton

#### Props
::TuffPropsTable
---
rows:
  - name: rows
    type: 'number'
    default: '1'
    description: 'Number of rows; at least 1.'
  - name: leading
    type: 'boolean'
    default: 'false'
    description: 'Reserves space for a leading icon.'
  - name: description
    type: 'boolean'
    default: 'false'
    description: 'Adds a narrower description bar under the title.'
  - name: trailing
    type: 'boolean'
    default: 'false'
    description: 'Reserves space for a trailing control (switch, button, chip).'
  - name: separated
    type: 'boolean'
    default: 'false'
    description: 'Draws a hairline between rows on the same pixel as the real list separator.'
  - name: titleWidth
    type: 'string | number'
    default: '38%'
    description: 'Base width of the title bar, varied slightly per row; numbers are px.'
  - name: descWidth
    type: 'string | number'
    default: '62%'
    description: 'Width of the description bar; numbers are px.'
---
::

### useDeferredLoading

Turns a raw loading flag into a skeleton flag that doesn't flicker.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `source` | `MaybeRefOrGetter<boolean>` | - | The raw loading flag. |
| `options.delay` | `number` | `150` | Milliseconds before the skeleton shows; data that arrives sooner never shows one. |
| `options.minDuration` | `number` | `400` | Minimum milliseconds the skeleton stays once shown. |
| Returns | `Ref<boolean>` | - | Whether to show the skeleton. |

## Overview

- `variant="circle"` fixes the radius at `999px` and ignores `radius`.
- `TxCardSkeleton` and `TxListItemSkeleton` have fixed shapes and no props, events, or slots.
- The root is `aria-hidden="true"`; the host region announces loading, for example with `aria-busy` or a polite live region.

## Technologies

- Source: `packages/tuffex/packages/components/src/skeleton/`.

<TuffDocSourceLink />
