Skip to main content
/
/
/
Loading state

Loading state

Introduction

Loading states matter almost just as much as the loaded state. Every time data is fetched, an action is triggered, or a page is navigated to, the user sees something, and that something should feel intentional. The goal is a stable, predictable UI with no layout jumps or sudden visual changes.

As a designer, think about loading states for every view and component that displays data. For every screen you design, ask yourself: what does this look like before the data arrives?

ScenarioPattern
Navigating to a page or loading section dataSkeleton
A button triggering a slow actionButton with spinner
File upload or known-progress operationProgressBar

Skeleton loading

When a user navigates to a new page, render skeletons for the content area. The skeletons should mirror the actual layout of the page so that content snaps in without any layout shift when it arrives.

Only skeleton the content that is actually being fetched. Things like page titles, navigation, and labels that are hardcoded into the design should show up immediately. Putting skeletons on static content just adds noise and hides information the user could already see.

Cards, tables, and sections that fetch their own data should show skeletons within their own bounds, without blocking the rest of the page. Match the number of skeletons to the number of items you expect, or at least 3-4 rows/items.

Avoid spinners for page and route loading

Spinners belong on buttons and actions, not on pages. Whether someone is clicking a link or loading the app for the first time, prefer skeletons. A full-page spinner blocks the whole interface and causes a layout jump when content finally loads.

The exception is large bulk operations or destructive processes where the UI should intentionally be blocked. For example, a multi-step data migration where partial interaction could cause errors. In those cases, a full-page spinner with a descriptive label is acceptable.

Ongoing projects

Project Alpha
Updated 2 days ago
Dashboard Beta
Updated 5 hours ago
Analytics View
Updated just now

Project team

NameStatusDate
Antonio Nusa
Active
16.06.2026
Erling Braut Haaland
Active
14.06.2026
Jørgen Strand Larsen
Inactive
10.06.2026

Action feedback

When a user clicks a button that triggers a slow action (saving, submitting, sending), set the button to its inactive state and show a spinner. This tells the user something is happening without disrupting the rest of the interface, and keeps the button accessible to screen readers — unlike a disabled button, which is hidden from assistive technology entirely.

Only add a loading indicator when the action takes more than 2-3 seconds.

When the action finishes, show a FloatingMessage to confirm the result. The button returning to its normal state is often too subtle on its own, and users may not notice whether the action succeeded or failed. Use state="success" on completion and state="alert" if something went wrong.

See the Button documentation for full guidance on button loading states, including icon-only buttons with spinner tooltips and custom inactive labels.

File uploads and known-progress operations

When progress is measurable, like during a file upload or a multi-step background job, use a ProgressBar with a descriptive label. This tells the user both that something is happening and how far along it is. Keep the rest of the UI functional while progress is underway.

0%

Uploading file...

See the ProgressBar documentation for guidance on labels and state colors. Also see the File input area documentation for file upload best practices.

Best practices

  • Always design a loading state alongside the loaded state. For every screen that displays data, think about what it should look like before the data arrives.
  • Keep skeletons faithful to the layout. A skeleton that looks nothing like the real content will still cause a layout jump when the content loads. Match the shape, size, and quantity of the actual content.
  • Don't mix patterns in the same context. Avoid showing a skeleton for some content and a spinner for adjacent content at the same time.
  • Avoid full-page spinners for navigation and data loading. They block the interface and cause a layout shift when content arrives. Reserve them for bulk operations where blocking interaction is intentional.