Feedback
Skeleton
Keep the shape of upcoming content visible while it loads.
Import
import { NeuSkeletonComponent } from '@neural-ui/native/skeleton';Examples
Import ExampleComponent into its parent and place <app-example /> inside your application’s root neu-theme. The preview supplies the device frame and safe areas; use the root setup from Installation in your app.
Content preview
Switch between loading placeholders and the loaded card.
View and copy the complete example
import { Component, signal } from '@angular/core';
import { NeuButtonComponent } from '@neural-ui/native/button';
import { NeuCardComponent } from '@neural-ui/native/card';
import { NeuSkeletonComponent } from '@neural-ui/native/skeleton';
import { View, Text, ScrollView } from '@ng-native/components';
@Component({
selector: 'app-example',
imports: [NeuButtonComponent, NeuCardComponent, NeuSkeletonComponent, View, Text, ScrollView],
template: `
<scroll-view class="usage-scroll" keyboardShouldPersistTaps="handled"><view class="usage-stack">
<view class="usage-section">
<neu-button [label]="loading() ? 'Show loaded content' : 'Show loading placeholders'" variant="outline" size="sm" (pressed)="loading.set(!loading())" />
@if (loading()) {
<view class="loading-card">
<view class="loading-header">
<neu-skeleton label="Loading profile photo" variant="circle" [width]="40" [height]="40" />
<view class="loading-name">
<neu-skeleton label="Loading author" [lines]="2" [height]="12" [gap]="8" lastLineWidth="55%" />
</view>
</view>
<neu-skeleton label="Loading project image" variant="rect" [height]="112" [borderRadius]="10" />
<neu-skeleton label="Loading project description" [lines]="3" [height]="12" [gap]="8" lastLineWidth="70%" />
</view>
} @else {
<neu-card title="Design review" description="Alex Morgan · Product team" variant="outlined"><text>The onboarding flow is ready for your feedback.</text></neu-card>
}
</view>
</view></scroll-view>
`,
styles: `
.actions { gap: 12px; align-items: flex-start; } text { color: var(--neu-text, #172033); font-size: 16px; }
.usage-scroll { flex: 1; }
.usage-stack { gap: 20px; padding-bottom: 20px; }
.usage-section { gap: 12px; }
.usage-heading { color: var(--neu-text, #172033); font-size: 16px; font-weight: 600; }
.usage-caption { color: var(--neu-muted, #64748b); font-size: 14px; }
.usage-row { flex-direction: row; flex-wrap: wrap; gap: 12px; align-items: center; }
.usage-actions { flex-direction: row; flex-wrap: wrap; gap: 8px; align-items: center; }
:host { width: 100%; } .loading-card { gap: 16px; } .loading-header { flex-direction: row; align-items: center; gap: 12px; } .loading-name { flex: 1; min-width: 0; }
`,
})
export class ExampleComponent {
readonly loading = signal(true);
}Without animation
Use static placeholders when motion is not needed.
View and copy the complete example
import { Component, signal } from '@angular/core';
import { NeuSkeletonComponent } from '@neural-ui/native/skeleton';
import { View, ScrollView } from '@ng-native/components';
@Component({
selector: 'app-example',
imports: [NeuSkeletonComponent, View, ScrollView],
template: `
<scroll-view class="usage-scroll" keyboardShouldPersistTaps="handled"><view class="usage-stack">
<view class="usage-section">
<neu-skeleton label="Static loading description" animation="none" [lines]="2" [height]="12" lastLineWidth="55%" />
</view>
</view></scroll-view>
`,
styles: `
.actions { gap: 12px; align-items: flex-start; } text { color: var(--neu-text, #172033); font-size: 16px; }
.usage-scroll { flex: 1; }
.usage-stack { gap: 20px; padding-bottom: 20px; }
.usage-section { gap: 12px; }
.usage-heading { color: var(--neu-text, #172033); font-size: 16px; font-weight: 600; }
.usage-caption { color: var(--neu-muted, #64748b); font-size: 14px; }
.usage-row { flex-direction: row; flex-wrap: wrap; gap: 12px; align-items: center; }
.usage-actions { flex-direction: row; flex-wrap: wrap; gap: 8px; align-items: center; }
:host { width: 100%; } .loading-card { gap: 16px; } .loading-header { flex-direction: row; align-items: center; gap: 12px; } .loading-name { flex: 1; min-width: 0; }
`,
})
export class ExampleComponent {
readonly loading = signal(true);
}Interactive playground
Change the settings and try the result.
Device captures shown in English.


API reference
Inputs
| Name | Type | Default | Binding | Description |
|---|---|---|---|---|
| label | string | 'Loading' | Input | Human-readable name for the control, action or group. |
| lines | number | 3 | Input | Number of text lines, clamped to 1–20. The rect and circle variants always render one shape. Nonfinite counts use three lines. |
| variant | NeuSkeletonVariant | 'text' | Input | The text variant renders repeated lines; rect and circle render one shape. Match the placeholder to the content that will replace it. |
| width | string | number | undefined | undefined | Input | Width in layout points (number or numeric string), px, or a percentage of the parent. Unset uses 100% for text/rect and the resolved height for circle. Use equal width and height for a circle; its radius remains fully rounded. Invalid values fall back to the automatic width and the wrapper never exceeds its parent. |
| height | number | undefined | undefined | Input | Height of each line or shape in layout points. Unset uses 16 for text/rect and 40 for circle. Negative values become zero. |
| borderRadius | number | 8 | Input | Corner radius in layout points for text and rect. Zero creates square corners. The circle variant always stays fully rounded. |
| gap | number | 12 | Input | Vertical separation between text lines in layout points. Zero joins the lines; it has no visible effect on a single shape. |
| lastLineWidth | string | number | '65%' | Input | Width of the last text line, in layout points, px or a percentage of the skeleton width. Use 100% for equally long lines. Does not affect rect or circle. |
| animation | NeuSkeletonAnimation | 'pulse' | Input | The pulse animation gently changes opacity; none keeps the placeholder static. System reduced motion and a zero-duration theme disable movement even when pulse is selected. |
Models
This component declares no two-way models.
Outputs
This component declares no public outputs.
Types and data interfaces
Use the imports in your application. The definitions below are provided for reference.
NeuSkeletonVariant
import type { NeuSkeletonVariant } from '@neural-ui/native/skeleton';
// Public definition for reference only. Import this type; do not redeclare it.
export type NeuSkeletonVariant = 'text' | 'rect' | 'circle';NeuSkeletonAnimation
import type { NeuSkeletonAnimation } from '@neural-ui/native/skeleton';
// Public definition for reference only. Import this type; do not redeclare it.
export type NeuSkeletonAnimation = 'pulse' | 'none';