Feedback Components
When to Use
Communicating async operation states (loading, progress) and skeleton screens during content fetching. These components are almost always paired with JavaScript state management — the CSS classes exist, but the show/hide logic requires code.
Decision: Which Feedback Component
| Component | Class | Use for |
|---|---|---|
| Spinner / animation | .loading |
Loading state indicator during async operations |
| Linear progress | .progress |
Determinate or indeterminate progress bar |
| Content placeholder | .skeleton |
Shape-matching placeholder during content fetch |
| Circular progress | .radial-progress |
Donut-style percentage display |
.loading — Loading Spinner / Animation
Description: An animated indicator for loading states. Six animation styles available.
Modifiers:
| Class | Animation style |
|---|---|
loading-spinner |
Rotating circle (default) |
loading-dots |
Three bouncing dots |
loading-ring |
Rotating ring (thinner than spinner) |
loading-ball |
Bouncing ball |
loading-bars |
Vertical equalizer bars |
loading-infinity |
Infinity loop |
Size modifiers: loading-xs loading-sm loading-md loading-lg loading-xl
Color modifiers: text-primary through text-error (uses currentColor)
<!-- Default spinner -->
<span class="loading loading-spinner loading-md"></span>
<!-- Inside a button -->
<button class="btn btn-primary" disabled>
<span class="loading loading-spinner loading-sm"></span>
Saving...
</button>
<!-- Full-page overlay -->
<div class="flex items-center justify-center h-screen">
<span class="loading loading-dots loading-xl text-primary"></span>
</div>
Gotchas:
.loadingrenders an<span>— it is an inline element. Use insideflexorgridcontainers for centering- No
aria-labelis added automatically — always addaria-label="Loading"androle="status"for screen readers - The animation plays immediately on render — control visibility via conditional rendering or
hiddenclass, not by removing the animation class
.progress — Linear Progress Bar
Description: A styled progress bar element. Supports both determinate (known value) and indeterminate (unknown duration) states.
Modifiers: progress-primary through progress-error
<!-- Determinate -->
<progress class="progress progress-primary w-56" value="60" max="100"></progress>
<!-- Indeterminate (animated sweep) -->
<progress class="progress w-56"></progress>
<!-- With label -->
<div class="w-56">
<div class="flex justify-between text-sm mb-1">
<span>Uploading</span>
<span>60%</span>
</div>
<progress class="progress progress-success w-full" value="60" max="100"></progress>
</div>
Gotchas:
- Omitting
valueattribute triggers the indeterminate animation — this is native HTML<progress>behavior <progress>is a void element — do not put content inside it- The progress bar width is controlled by Tailwind
w-*utilities —progresshas no intrinsic width
.skeleton — Content Placeholder
Description: A pulsing grey placeholder that mimics content shape during loading. Pure CSS — apply the class to any element with explicit dimensions.
<!-- Text placeholder -->
<div class="skeleton h-4 w-48"></div>
<!-- Card skeleton -->
<div class="card w-64 bg-base-100 shadow">
<div class="skeleton h-32 w-full"></div>
<div class="card-body gap-3">
<div class="skeleton h-4 w-36"></div>
<div class="skeleton h-4 w-24"></div>
<div class="flex gap-2 mt-2">
<div class="skeleton h-8 w-20"></div>
<div class="skeleton h-8 w-20"></div>
</div>
</div>
</div>
<!-- Avatar + text row skeleton -->
<div class="flex items-center gap-4">
<div class="skeleton w-12 h-12 rounded-full"></div>
<div class="flex flex-col gap-2">
<div class="skeleton h-4 w-32"></div>
<div class="skeleton h-4 w-24"></div>
</div>
</div>
Gotchas:
.skeletonrequires explicit dimensions (h-*andw-*) — it has no intrinsic size- The pulse animation uses
opacity— do not setopacityutilities on skeleton elements - Swap skeleton elements for real content by conditional rendering (React) or CSS class toggling — DaisyUI provides no show/hide mechanism
- Use
rounded-fullon skeleton avatar circles to match the real content's border-radius
.radial-progress — Circular Progress Indicator
Description: A circular/donut progress indicator. Value is set via CSS custom property --value.
Modifiers: text-primary through text-error (track and indicator color)
CSS variables:
| Variable | Default | Effect |
|---|---|---|
--value |
— | Required. Progress percentage (0–100) |
--size |
3.5rem |
Circle diameter |
--thickness |
derived | Ring stroke width |
<div class="radial-progress text-primary" style="--value:70;" role="progressbar" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100">
70%
</div>
<div class="radial-progress text-success" style="--value:100; --size:8rem; --thickness:0.5rem;" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100">
Done
</div>
Gotchas:
--valuemust be set as an inline style (or CSS) — not a Tailwind class- Text inside
.radial-progressis centered automatically — put the percentage label as inner content - Always add
role="progressbar"witharia-valuenow,aria-valuemin,aria-valuemax— DaisyUI does not add ARIA attributes - The color modifier (
text-primary) sets both the ring color and the track color — use[--tw-ring-color:...]overrides to separate them if needed
Common Mistakes
- Rendering
.loadingwithoutaria-label="Loading"androle="status"— invisible to screen readers - Using
.skeletonwithout explicith-*andw-*— it renders as zero-size - Setting
--valueon.radial-progressas a Tailwind class instead of inline style — CSS custom properties requirestyle=""or a@layeroverride - Leaving
.progressin indeterminate state when the operation is complete — swap to a determinate value or hide the element
See Also
- Data Display Components —
.toastfor success/error notifications after async operations complete - Actions Components — disabling
.btnand adding.loadingspinner inside it during form submission - Reference: https://daisyui.com/components/loading/
- Reference: https://daisyui.com/components/progress/
- Reference: https://daisyui.com/components/skeleton/