# Skeleton
*Create elegant loading placeholders that mimic your content structure to improve perceived performance during data loading.*
The `Skeleton` [widget](../../01_Onboarding/02_Concepts/03_Widgets.md) creates placeholder loading indicators that mimic the shape of your content. It improves perceived performance by showing users the layout of the page while data is loading. Use [Size](../../04_ApiReference/IvyShared/Size.md) for `.Height()` and `.Width()` to match your content layout.
```csharp
public class ProductCardView : ViewBase
{
public record ProductData(
string Name,
string Description,
decimal Price,
string ImageUrl,
double Rating,
int Stock);
public override object? Build()
{
var isLoading = UseState(false);
var product = UseState<ProductData?>();
var loadTrigger = UseState(0);
// Handle loading with proper timer cleanup
UseEffect(() => {
if (loadTrigger.Value > 0)
{
isLoading.Set(true);
product.Set((ProductData?)null);
// Create timer with proper disposal
var timer = new System.Threading.Timer(_ => {
product.Set(new ProductData(
"Premium Wireless Headphones",
"Experience crystal-clear sound with our premium noise-cancelling wireless headphones. Features 30-hour battery life and memory foam ear cushions for all-day comfort.",
149.99m,
"https://png.pngtree.com/png-vector/20250703/ourmid/pngtree-black-headphones-sleek-3d-render-png-image_16600605.webp",
4.7,
12
));
isLoading.Set(false);
}, null, 2000, Timeout.Infinite);
// Return cleanup function to dispose timer
return timer;
}
return null;
}, [loadTrigger]);
void LoadProduct()
{
loadTrigger.Set(t => t + 1);
}
return Layout.Vertical().Gap(4).Padding(4)
| new Button(
isLoading.Value ? "Loading..." : "Load Product Data",
onClick: _ => LoadProduct()
).Disabled(isLoading.Value)
| (product.Value != null || isLoading.Value
? (isLoading.Value
? Layout.Vertical().Gap(3)
| new Skeleton().Height(Size.Units(40)).Width(Size.Full())
| new Skeleton().Height(Size.Units(24)).Width(Size.Units(40))
| new Skeleton().Height(Size.Units(16)).Width(Size.Full())
| new Skeleton().Height(Size.Units(16)).Width(Size.Full())
| new Skeleton().Height(Size.Units(16)).Width(Size.Units(40))
| (Layout.Horizontal().Gap(2)
| new Skeleton().Height(Size.Units(24)).Width(Size.Units(40))
| new Skeleton().Height(Size.Units(36)).Width(Size.Units(40)))
: Layout.Vertical().Gap(3)
| new Image("https://png.pngtree.com/png-vector/20250703/ourmid/pngtree-black-headphones-sleek-3d-render-png-image_16600605.webp")
.Height(Size.Units(40))
| Text.H3(product.Value?.Name)
| Text.P(product.Value?.Description)
| Text.Strong($"Rating: {product.Value?.Rating}/5")
| (Layout.Horizontal().Gap(2)
| Text.H4($"${product.Value?.Price}")
| new Spacer().Width(Size.Grow())
| new Button("Add to Cart")))
: Text.P("Click the button above to load product data and see the skeleton loading state."));
}
}
```
## API
[View Source: Skeleton.cs](https://github.com/Ivy-Interactive/Ivy-Framework/blob/main/src/Ivy/Widgets/Primitives/Skeleton.cs)
### Constructors
| Signature |
|-----------|
| `new Skeleton()` |
### Properties
| Name | Type | Setters |
|------|------|---------|
| `Height` | `Size` | - |
| `Scale` | `Scale?` | - |
| `Visible` | `bool` | - |
| `Width` | `Size` | - |