Once UI Handbook
Beginner Guides
What is Once UI?
Install & config
Page skeleton
Your first page
Build And Launch
How to launch a portfolio site that actually gets you hired
How to build a documentation site with MDX and Next.js
How to ship a landing page and dashboard with authentication in Next.js
How to launch a social app with Next.js and Supabase
Vibe Coding
Introduction to vibe coding
Set up your local dev environment
Essential tools for vibe coding
Build your first Once UI app
Common Patterns
Hero section
Static panel
Form layout
Highlighted card
Responsive stacking
Decoration layers
Dialog & modal
Toast & feedback
Tables & lists
Loading states
App shell navigation
Dashboard & charts
Chat & messaging
Auth & verification
Settings & split panels
Data filters & toolbar
Command palette
Pricing & plans
Accordion & FAQ
Media & uploads
Empty & error states
SEO, Open Graph, and structured data
Documentation code blocks with live preview
Carousels, galleries, and before/after comparisons
Date pickers and scheduling
Context menus and dropdown actions
Tags, chips, and multi-value inputs
Roadmap & kanban
Form inputs & controls
Progress, status & badges
Scrolling & feeds
Profile, avatars & identity
Social proof & logo clouds
Masonry & media grids
Table pagination, search & bulk selection
Banners & announcements
Footer layouts
Onboarding & first run
Waitlist & coming soon
Design Tips
Color & surfaces
Harness overview
Row, Column & Grid
Spacing & rhythm
Icons & buttons
Reveal & motion
Theme tokens, FOUC-free init, and runtime style controls
Tooltips and hover cards
Block updates
Block updates changelog
Agent Resources
Column, not Flex
Semantics over CSS
Typography scale
Common gotchas
Block anchors
Validate generated code
Pro block registry
Match task bundles
TrademarkTrademark
Ctrl k
Search...
Sign up
Once UI Handbook
Beginner Guides
What is Once UI?
Install & config
Page skeleton
Your first page
Build And Launch
How to launch a portfolio site that actually gets you hired
How to build a documentation site with MDX and Next.js
How to ship a landing page and dashboard with authentication in Next.js
How to launch a social app with Next.js and Supabase
Vibe Coding
Introduction to vibe coding
Set up your local dev environment
Essential tools for vibe coding
Build your first Once UI app
Common Patterns
Hero section
Static panel
Form layout
Highlighted card
Responsive stacking
Decoration layers
Dialog & modal
Toast & feedback
Tables & lists
Loading states
App shell navigation
Dashboard & charts
Chat & messaging
Auth & verification
Settings & split panels
Data filters & toolbar
Command palette
Pricing & plans
Accordion & FAQ
Media & uploads
Empty & error states
SEO, Open Graph, and structured data
Documentation code blocks with live preview
Carousels, galleries, and before/after comparisons
Date pickers and scheduling
Context menus and dropdown actions
Tags, chips, and multi-value inputs
Roadmap & kanban
Form inputs & controls
Progress, status & badges
Scrolling & feeds
Profile, avatars & identity
Social proof & logo clouds
Masonry & media grids
Table pagination, search & bulk selection
Banners & announcements
Footer layouts
Onboarding & first run
Waitlist & coming soon
Design Tips
Color & surfaces
Harness overview
Row, Column & Grid
Spacing & rhythm
Icons & buttons
Reveal & motion
Theme tokens, FOUC-free init, and runtime style controls
Tooltips and hover cards
Block updates
Block updates changelog
Agent Resources
Column, not Flex
Semantics over CSS
Typography scale
Common gotchas
Block anchors
Validate generated code
Pro block registry
Match task bundles
TrademarkTrademark
Once UIDocumentationBlog
© Once UI. All rights reserved.
Built with Aveiro

Loading states

Skeleton placeholders for content areas, spinners for actions — keep layout stable while data loads.
Updated 19d ago
Tables & lists
App shell navigation
4 min · Pattern
Skeleton placeholders preserve layout while data loads. Spinners signal in-flight actions. Use both — skeletons for content areas, spinners for buttons and inline fetches.

Skeleton vs spinner

State
Component
Example
Page section loadingSkeleton shape propsDashboard cards, table rows, profile header
Button action in progressButton `loading` propSave, submit, delete
Full-page blocking loadCentered spinner in ColumnInitial route fetch
Background refreshNone — keep stale content visibleSettings panel re-fetch
Skeletons hold space so the layout does not jump when real content arrives. The loading prop on buttons signals activity on an element the user just interacted with.

Skeleton shapes

The skeleton component has three shapes:
Shape
Use for
`line` (default)Text lines, table rows
`circle`Avatars, icon placeholders
`block`Images, chart areas, media cards
Build a profile placeholder with a circle on top and two lines below. Use t-shirt width and height sizes (xs through xl), not pixel values. A media card placeholder uses shape="block" with width="fill" and height="l".

Stagger with delay

Offset shimmer with the delay prop ("1" through "6") when rendering a list of placeholders. Map over four items and pass delay={String(index + 1)} on each row's skeleton children. Match the skeleton layout to the real content it replaces — same gaps, same approximate widths.

Table skeleton

Mirror your table structure with skeleton rows inside the same static panel:
  • Wrap in a Column surface panel (padding="24", background="surface", border="neutral-alpha-weak", radius="l")
  • One line skeleton for the section title
  • Five line skeletons with width="fill" and staggered delay
Swap the skeleton block for your table component once rows are ready.

Button loading state

Pass loading={isSaving} to Button — it swaps the label for a built-in spinner automatically. Do not nest a manual spinner inside a button unless you need a custom layout.

Write this / not this

✅ Once UI way
❌ Common mistake
Skeleton matching final layoutGeneric gray div with inline styles
`Button loading={true}`Disabled button with no feedback
Keep stale content during refreshFull-page spinner on every re-fetch
`delay` stagger on listsAll skeletons animating in sync

Check yourself

  • Skeleton shape matches what it replaces (circle for avatar, block for image)
  • Layout gaps are identical between skeleton and loaded state
  • Buttons use the loading prop, not a separate overlay
  • Empty state shown after load completes with zero results

Related

→ Tables & lists → Toast & feedback → Full reference: docs.once-ui.com — Skeleton