Introduction
Components
- Accordion
- Action Sheet
- Alert
- Audio Player
- Audio Recorder
- Audio Waveform
- Avatar
- Badge
- BottomSheet
- Button
- Camera
- Camera Preview
- Card
- Carousel
- Checkbox
- Collapsible
- Color Picker
- Combobox
- Date Picker
- File Picker
- Gallery
- Hello Wave
- Icon
- Image
- Input
- Input OTP
- Link
- MediaPicker
- Mode Toggle
- Onboarding
- ParallaxScrollView
- Picker
- Popover
- Progress
- Radio
- ScrollView
- SearchBar
- Separator
- Share
- Sheet
- Skeleton
- Spinner
- Switch
- Table
- Tabs
- Text
- Toast
- Toggle
- Video
- View
Charts
import { Spinner } from '@/components/ui/spinner';
import React from 'react';
export function SpinnerDemo() {
return <Spinner size='default' variant='default' />;
}
Installation
pnpm dlx bna-ui add spinner
Usage
import {
Spinner,
LoadingOverlay,
InlineLoader,
ButtonSpinner,
} from '@/components/ui/spinner';
<Spinner size='default' variant='default' />
Examples
Default
import { Spinner } from '@/components/ui/spinner';
import React from 'react';
export function SpinnerDemo() {
return <Spinner size='default' variant='default' />;
}
Variants
import { Spinner } from '@/components/ui/spinner';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React from 'react';
export function SpinnerVariants() {
const variants = [
{ variant: 'default' as const, label: 'Default' },
{ variant: 'cirlce' as const, label: 'Circle' },
{ variant: 'dots' as const, label: 'Dots' },
{ variant: 'pulse' as const, label: 'Pulse' },
{ variant: 'bars' as const, label: 'Bars' },
];
return (
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 24 }}>
{variants.map(({ variant, label }) => (
<View key={variant} style={{ alignItems: 'center', gap: 8 }}>
<Spinner variant={variant} size='default' />
<Text variant='caption' style={{ textAlign: 'center' }}>
{label}
</Text>
</View>
))}
</View>
);
}
Sizes
import { Spinner } from '@/components/ui/spinner';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React from 'react';
export function SpinnerSizes() {
const sizes = [
{ size: 'sm' as const, label: 'Small' },
{ size: 'default' as const, label: 'Default' },
{ size: 'lg' as const, label: 'Large' },
{ size: 'icon' as const, label: 'Icon' },
];
return (
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 32 }}>
{sizes.map(({ size, label }) => (
<View key={size} style={{ alignItems: 'center', gap: 8 }}>
<Spinner size={size} variant='cirlce' />
<Text variant='caption' style={{ textAlign: 'center' }}>
{label}
</Text>
</View>
))}
</View>
);
}
With Labels
import { Spinner } from '@/components/ui/spinner';
import { View } from '@/components/ui/view';
import React from 'react';
export function SpinnerLabels() {
return (
<View style={{ gap: 24 }}>
<Spinner size='default' variant='default' showLabel />
<Spinner size='default' variant='dots' label='Processing...' />
<Spinner size='default' variant='pulse' label='Uploading files...' />
<Spinner size='lg' variant='cirlce' label='Please wait' />
</View>
);
}
Speed Control
import { Spinner } from '@/components/ui/spinner';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React from 'react';
export function SpinnerSpeeds() {
const speeds = [
{ speed: 'slow' as const, label: 'Slow' },
{ speed: 'normal' as const, label: 'Normal' },
{ speed: 'fast' as const, label: 'Fast' },
];
return (
<View style={{ flexDirection: 'row', gap: 32 }}>
{speeds.map(({ speed, label }) => (
<View key={speed} style={{ alignItems: 'center', gap: 8 }}>
<Spinner variant='cirlce' size='default' speed={speed} />
<Text variant='caption' style={{ textAlign: 'center' }}>
{label}
</Text>
</View>
))}
</View>
);
}
Custom Colors
import { Spinner } from '@/components/ui/spinner';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React from 'react';
export function SpinnerColors() {
const colors = [
{ color: '#3b82f6', label: 'Blue', variant: 'default' as const },
{ color: '#10b981', label: 'Green', variant: 'dots' as const },
{ color: '#f59e0b', label: 'Orange', variant: 'pulse' as const },
{ color: '#ef4444', label: 'Red', variant: 'bars' as const },
{ color: '#8b5cf6', label: 'Purple', variant: 'cirlce' as const },
];
return (
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 24 }}>
{colors.map(({ color, label, variant }) => (
<View key={color} style={{ alignItems: 'center', gap: 8 }}>
<Spinner variant={variant} size='default' color={color} />
<Text variant='caption' style={{ textAlign: 'center' }}>
{label}
</Text>
</View>
))}
</View>
);
}
Loading Overlay
import { Button } from '@/components/ui/button';
import { LoadingOverlay } from '@/components/ui/spinner';
import { View } from '@/components/ui/view';
import React, { useState } from 'react';
export function SpinnerOverlay() {
const [showOverlay, setShowOverlay] = useState(false);
const handleShowOverlay = () => {
setShowOverlay(true);
// Auto hide after 3 seconds for demo
setTimeout(() => setShowOverlay(false), 3000);
};
return (
<View style={{ gap: 16 }}>
<Button onPress={handleShowOverlay} disabled={showOverlay}>
Show Loading Overlay
</Button>
<LoadingOverlay
visible={showOverlay}
size='lg'
variant='cirlce'
label='Loading content...'
backdrop={true}
backdropOpacity={0.7}
/>
</View>
);
}
Inline Loader
import { InlineLoader } from '@/components/ui/spinner';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React from 'react';
export function SpinnerInline() {
return (
<View style={{ gap: 16 }}>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
<Text>Loading data</Text>
<InlineLoader size='sm' variant='default' />
</View>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
<Text>Processing</Text>
<InlineLoader size='sm' variant='dots' />
</View>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
<InlineLoader size='sm' variant='pulse' color='#10b981' />
<Text>Syncing...</Text>
</View>
</View>
);
}
API Reference
Spinner
The main spinner component with multiple variants and customization options.
Prop | Type | Default | Description |
---|---|---|---|
size | 'default' | 'sm' | 'lg' | 'icon' | 'default' | The size of the spinner. |
variant | 'default' | 'cirlce' | 'dots' | 'pulse' | 'bars' | 'default' | The visual variant of the spinner. |
label | string | - | Optional label text to display with spinner. |
showLabel | boolean | false | Whether to show the default "Loading..." label. |
style | ViewStyle | - | Additional styles to apply to the container. |
color | string | - | Custom color for the spinner. |
speed | 'slow' | 'normal' | 'fast' | 'normal' | Animation speed of the spinner. |
LoadingOverlay
A full-screen overlay component with spinner for blocking UI interactions during loading.
Prop | Type | Default | Description |
---|---|---|---|
visible | boolean | - | Whether the overlay is visible. |
backdrop | boolean | true | Whether to show a backdrop behind the spinner. |
backdropColor | string | - | Custom backdrop color. |
backdropOpacity | number | 0.5 | Opacity of the backdrop (0-1). |
onRequestClose | () => void | - | Callback when overlay should be closed. |
...spinnerProps | SpinnerProps | - | All props from the Spinner component. |
InlineLoader
A compact spinner optimized for inline usage within text or small containers.
Prop | Type | Default | Description |
---|---|---|---|
size | 'default' | 'sm' | 'lg' | 'icon' | 'sm' | The size of the spinner. |
variant | 'default' | 'cirlce' | 'dots' | 'pulse' | 'bars' | 'default' | The visual variant of the spinner. |
color | string | - | Custom color for the spinner. |
ButtonSpinner
A spinner component specifically designed for button loading states.
Prop | Type | Default | Description |
---|---|---|---|
size | 'default' | 'sm' | 'lg' | 'icon' | 'sm' | The size of the spinner. |
variant | 'default' | 'cirlce' | 'dots' | 'pulse' | 'bars' | 'default' | The visual variant of the spinner. |
color | string | - | Custom color for the spinner. |
Accessibility
The Spinner component is built with accessibility in mind:
- Provides meaningful loading states for screen readers
- Supports custom labels for better context
- Maintains proper contrast ratios for visibility
- Non-intrusive animations that respect user preferences
- Loading overlays properly manage focus and interaction states