Patterns
Inline Status Row
When to use:
- one action is pending and nearby text can explain it
Composition notes:
- pair the spinner with short status text
- keep the size small
Trade-offs:
- compact and clear
- depends on nearby copy for meaning
import { Spinner, Text } from '@vega-ui/react';
import { useId } from 'react';
export default function InlineStatusRow() {
const labelId = useId();
return (
<div style={{ display: 'flex', gap: 'var(--spacing-4)', alignItems: 'center' }}>
<Spinner size={2} aria-labelledby={labelId} />
<Text id={labelId} size={2}>Syncing 128 contacts from Google</Text>
</div>
);Centered Surface Loader
When to use:
- a panel or section is loading and content is not yet ready
Composition notes:
- center the spinner within a reserved area
- choose a larger size than inline usage
Trade-offs:
- easy to notice
- removes content detail while loading
import {
Button,
Dialog,
DialogClose,
DialogContent,
DialogTrigger,
Heading,
Icon,
IconButton,
Spinner,
Text,
} from '@vega-ui/react';Button Pending State
When to use:
- an action is in progress and the button is temporarily disabled
Composition notes:
- keep the spinner compact
- disable the button while pending
Trade-offs:
- clear immediate feedback
- if text disappears entirely, context may be weaker
import { Button, HelperText, Label, Spinner, TextField, TextFieldInput } from '@vega-ui/react';
import { CSSProperties, FormEvent, useId, useState } from 'react';
export default function ButtonPendingState() {
const fieldId = useId();
const [pending, setPending] = useState(false);
const [message, setMessage] = useState('');
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
const workspace = String(data.get('workspace'));Comparison
Quick Decision Rule
Use Spinner when a temporary pending state needs a compact animated indicator. Do not use it as a substitute for preserving layout or showing final status.
Spinner vs retained content
Use Spinner when:
- progress is brief and the action is already obvious
Keep content in place when:
- preserving layout matters more than motion
Spinner vs progress indicators
Use Spinner when:
- exact progress is unknown
Use Progress when:
- progress can be quantified
Choose This Component When
- loading is indeterminate
- a compact indicator is enough
- the UI does not need to show a numeric or bar-like amount of completion
Do Not Choose This Component When
- exact progress is known
- the UI needs more context than a small animated ring can provide
Troubleshooting
Users Cannot Tell What Is Loading
Symptom
The spinner is visible, but users do not know which operation is pending.
Likely Cause
The spinner was rendered without contextual text or surrounding structure.
How To Verify
- view the UI without knowing the implementation details
- check whether the pending operation is obvious from nearby content
Fix
Pair the spinner with status text or place it inside an unmistakable pending surface.
The Spinner Feels Too Large Or Too Small
Symptom
The loading indicator looks visually unbalanced in its container.
Likely Cause
The numeric size does not match the surrounding control or surface.
How To Verify
- compare the current size with adjacent text and controls
Fix
Use smaller sizes for inline and button contexts, and larger sizes for centered section loaders.
Motion Alone Is Carrying The Loading State
Symptom
Users who miss or cannot rely on motion do not get enough status information.
Likely Cause
The spinner is the only pending-state signal.
How To Verify
- remove motion mentally and check whether any status text remains
Fix
Add nearby status text or a higher-level live region for meaningful asynchronous operations.