Skip to content

Latest commit

 

History

History
772 lines (572 loc) · 14.4 KB

File metadata and controls

772 lines (572 loc) · 14.4 KB

State Components Guide

Complete guide to using empty states, error states, loading states, success states, and confirmation dialogs in the Marriott Voyage app.

Table of Contents


Empty States

Empty states appear when there's no content to display. They guide users on what to do next.

Base EmptyState Component

import { EmptyState } from '@/components/states';

<EmptyState
  icon="📝"
  title="No posts yet"
  description="Share your travel experiences"
  buttonText="Create Post"
  onButtonPress={() => navigation.navigate('CreatePost')}
/>

Pre-configured Empty State Variants

NoFriendsEmpty

import { NoFriendsEmpty } from '@/components/states';

<NoFriendsEmpty
  onButtonPress={() => navigation.navigate('FindFriends')}
/>

Icon: 👥 Title: "No friends yet" Description: "Start connecting with fellow travelers" Button: "Find Friends"

NoMessagesEmpty

<NoMessagesEmpty
  onButtonPress={() => navigation.navigate('NewMessage')}
/>

Icon: 💬 Title: "No messages" Description: "Your conversations will appear here"

NoBookingsEmpty

<NoBookingsEmpty
  onButtonPress={() => navigation.navigate('Events')}
/>

Icon: 🎟️ Title: "No bookings yet" Description: "Discover events and activities"

NoPostsEmpty

<NoPostsEmpty
  onButtonPress={() => navigation.navigate('CreatePost')}
/>

Icon: 📝 Title: "No posts yet" Description: "Share your travel experiences"

SearchNoResults

<SearchNoResults
  onButtonPress={() => setSearchQuery('')}
/>

Icon: 🔍 Title: "No results found" Description: "Try a different search term"

Other Empty States

  • NoNotificationsEmpty - No notifications
  • NoPlansEmpty - No plans created
  • NoMatchesEmpty - No traveler matches
  • NoReviewsEmpty - No reviews yet
  • NoPhotosEmpty - No photos uploaded
  • NoBadgesEmpty - No badges earned
  • NoSavedItemsEmpty - No saved items

Error States

Error states appear when something goes wrong. They explain the issue and offer recovery options.

Base ErrorState Component

import { ErrorState } from '@/components/states';

<ErrorState
  icon="⚠️"
  title="Something went wrong"
  description="We're working on fixing this"
  buttonText="Try Again"
  onButtonPress={handleRetry}
  variant="error" // 'error' | 'warning' | 'info'
/>

Pre-configured Error State Variants

NetworkError

import { NetworkError } from '@/components/states';

<NetworkError onRetry={refetch} />

Icon: 📡 Title: "No internet connection" Description: "Check your connection and try again"

GenericError

<GenericError onRetry={refetch} />

Icon: ⚠️ Title: "Something went wrong" Description: "We're working on fixing this"

NotFoundError

<NotFoundError onRetry={() => navigation.goBack()} />

Icon: 🤷 Title: "Page not found" Description: "This content may have been removed"

Other Error States

  • ServerError - Server issues
  • PermissionDeniedError - Access denied
  • TimeoutError - Request timeout
  • MaintenanceError - Under maintenance
  • LocationPermissionError - Location access required
  • CameraPermissionError - Camera access required
  • AgeRestrictedError - Age restricted content

Inline Errors

Inline errors appear within forms and UI elements for immediate feedback.

FieldError

For form field validation errors:

import { FieldError } from '@/components/states';

<TextInput
  value={email}
  onChangeText={setEmail}
  placeholder="Email"
/>
<FieldError
  message="Please enter a valid email address"
  visible={!!emailError}
/>

BannerError

For important errors at the top of the screen:

import { BannerError } from '@/components/states';

<BannerError
  message="Failed to save changes. Please try again."
  visible={showError}
/>

InlineWarning

For non-critical warnings:

import { InlineWarning } from '@/components/states';

<InlineWarning
  message="Your profile is incomplete. Complete it to get more matches."
  visible={!isProfileComplete}
/>

ApiError

For API/network errors:

import { ApiError } from '@/components/states';

<ApiError
  message="Failed to load events. Please check your connection and try again."
  visible={!!apiError}
/>

ValidationSummary

For multiple form errors:

import { ValidationSummary } from '@/components/states';

<ValidationSummary
  errors={[
    'Name is required',
    'Email must be valid',
    'Password must be at least 8 characters'
  ]}
  visible={errors.length > 0}
/>

Loading States

Loading states show progress and keep users informed during operations.

FullScreenLoader

For initial app loading or major operations:

import { FullScreenLoader } from '@/components/states';

{isLoading && (
  <FullScreenLoader message="Loading your profile..." />
)}

InlineLoader

For small loading indicators:

import { InlineLoader } from '@/components/states';

<InlineLoader message="Sending..." />

LoadingOverlay

For blocking operations with semi-transparent overlay:

import { LoadingOverlay } from '@/components/states';

{isProcessing && (
  <LoadingOverlay message="Processing payment..." />
)}

LoadingMoreFooter

For FlatList pagination:

import { LoadingMoreFooter } from '@/components/states';

<FlatList
  data={items}
  renderItem={renderItem}
  ListFooterComponent={
    isLoadingMore ? <LoadingMoreFooter /> : null
  }
/>

RefreshingHeader

For pull-to-refresh:

import { RefreshingHeader } from '@/components/states';

<FlatList
  data={items}
  renderItem={renderItem}
  refreshing={isRefreshing}
  onRefresh={handleRefresh}
  ListHeaderComponent={
    isRefreshing ? <RefreshingHeader /> : null
  }
/>

ContentLoader

For content placeholder while loading:

import { ContentLoader } from '@/components/states';

{isLoading ? (
  <ContentLoader />
) : (
  <View>
    <Text>{content}</Text>
  </View>
)}

UploadingProgress

For file uploads with progress:

import { UploadingProgress } from '@/components/states';

<UploadingProgress
  progress={uploadProgress} // 0-100
  message="Uploading photo..."
/>

ProcessingLoader

For operations like image processing:

import { ProcessingLoader } from '@/components/states';

{isProcessing && (
  <ProcessingLoader message="Processing image..." />
)}

Success States

Success states celebrate user achievements and provide positive feedback.

SuccessModal

For successful operations:

import { SuccessModal } from '@/components/states';

<SuccessModal
  visible={showSuccess}
  title="Booking Confirmed!"
  message="Check your email for details"
  buttonText="View Booking"
  onClose={() => {
    setShowSuccess(false);
    navigation.navigate('Bookings');
  }}
  autoClose={false}
/>

With auto-close:

<SuccessModal
  visible={showSuccess}
  title="Message Sent"
  onClose={() => setShowSuccess(false)}
  autoClose={true}
  autoCloseDelay={2000}
/>

InlineSuccess

For inline success messages:

import { InlineSuccess } from '@/components/states';

<InlineSuccess
  message="Profile updated successfully"
  visible={showSuccess}
/>

PointsEarned

For gamification and rewards:

import { PointsEarned } from '@/components/states';

<PointsEarned
  visible={showPoints}
  points={50}
  reason="Completed your profile"
  onClose={() => setShowPoints(false)}
/>

BadgeEarned

For badge achievements:

import { BadgeEarned } from '@/components/states';

<BadgeEarned
  visible={showBadge}
  badgeName="Social Butterfly"
  badgeIcon="🦋"
  description="Made 10 new friends"
  onClose={() => setShowBadge(false)}
/>

Confirmation Dialogs

Confirmation dialogs ask users to confirm important or destructive actions.

Custom Confirmation Dialog

import { ConfirmationDialog } from '@/components/states';

<ConfirmationDialog
  visible={showDialog}
  title="Delete Post"
  message="Are you sure you want to delete this post?"
  confirmText="Delete"
  cancelText="Cancel"
  onConfirm={handleDelete}
  onCancel={() => setShowDialog(false)}
  variant="danger" // 'danger' | 'warning' | 'info'
  icon="📝"
/>

Native Alert (iOS/Android)

For native platform dialogs:

import { showNativeConfirmation } from '@/components/states';

showNativeConfirmation({
  title: 'Log Out',
  message: 'Are you sure you want to log out?',
  confirmText: 'Log Out',
  cancelText: 'Cancel',
  onConfirm: handleLogout,
  onCancel: () => {},
});

Pre-configured Confirmations

The ConfirmationDialogs object provides pre-configured confirmations:

Remove Friend

import { ConfirmationDialogs } from '@/components/states';

ConfirmationDialogs.removeFriend(
  'John Doe',
  () => handleRemoveFriend(),
  () => {}
);

Cancel Booking

ConfirmationDialogs.cancelBooking(
  () => handleCancelBooking(),
  () => {}
);

Delete Post

ConfirmationDialogs.deletePost(
  () => handleDeletePost(),
  () => {}
);

Logout

ConfirmationDialogs.logout(
  () => handleLogout(),
  () => {}
);

Block User

ConfirmationDialogs.blockUser(
  'Jane Smith',
  () => handleBlockUser(),
  () => {}
);

Leave Plan

ConfirmationDialogs.leavePlan(
  'Beach Volleyball',
  () => handleLeavePlan(),
  () => {}
);

Delete Account

ConfirmationDialogs.deleteAccount(
  () => handleDeleteAccount(),
  () => {}
);

Discard Changes

ConfirmationDialogs.discardChanges(
  () => handleDiscard(),
  () => {}
);

Best Practices

Empty States

  1. Always provide a CTA: Guide users on what to do next
  2. Use friendly, encouraging language: "Start connecting" instead of "No data"
  3. Add relevant icons: Make empty states visually appealing
  4. Context matters: Explain why the state is empty

Error States

  1. Be specific: Tell users exactly what went wrong
  2. Offer solutions: Provide retry buttons or alternative actions
  3. Use appropriate variants: danger for errors, warning for caution, info for FYI
  4. Don't blame the user: "Connection failed" instead of "You're offline"

Loading States

  1. Show progress when possible: Use progress bars for uploads
  2. Keep users informed: Add descriptive messages
  3. Don't overuse: Only show loading for operations >300ms
  4. Use skeletons for content: Better UX than spinners

Success States

  1. Celebrate achievements: Use animations and haptics
  2. Be brief: Auto-close for minor successes
  3. Provide next steps: Guide users after success
  4. Make it feel special: Use confetti, animations for big wins

Confirmation Dialogs

  1. Only for important actions: Don't overuse confirmations
  2. Be clear about consequences: Explain what will happen
  3. Use destructive style for dangerous actions: Red/danger variant
  4. Provide clear options: "Delete" vs "Cancel", not "Yes" vs "No"

Usage Examples

Complete Form Flow

function ProfileForm() {
  const [name, setName] = useState('');
  const [errors, setErrors] = useState<string[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [showSuccess, setShowSuccess] = useState(false);

  const handleSubmit = async () => {
    // Validation
    const validationErrors = [];
    if (!name) validationErrors.push('Name is required');
    if (validationErrors.length > 0) {
      setErrors(validationErrors);
      return;
    }

    // Loading
    setIsLoading(true);

    try {
      await updateProfile({ name });

      // Success
      setIsLoading(false);
      setShowSuccess(true);
    } catch (error) {
      // Error
      setIsLoading(false);
      showErrorToast({
        title: 'Failed to update profile',
        message: error.message,
      });
    }
  };

  return (
    <View>
      {/* Validation errors */}
      <ValidationSummary errors={errors} visible={errors.length > 0} />

      {/* Form field */}
      <TextInput
        value={name}
        onChangeText={setName}
        placeholder="Name"
      />
      <FieldError
        message="Name is required"
        visible={errors.includes('Name is required')}
      />

      {/* Submit button */}
      <AnimatedButton
        title="Save"
        onPress={handleSubmit}
        loading={isLoading}
      />

      {/* Success modal */}
      <SuccessModal
        visible={showSuccess}
        title="Profile Updated"
        message="Your changes have been saved"
        onClose={() => setShowSuccess(false)}
        autoClose={true}
      />
    </View>
  );
}

List with States

function EventsList() {
  const { data, loading, error, refetch, fetchMore } = useEvents();

  if (loading && !data) {
    return <FullScreenLoader message="Loading events..." />;
  }

  if (error) {
    return <NetworkError onRetry={refetch} />;
  }

  if (data?.length === 0) {
    return (
      <NoBookingsEmpty
        onButtonPress={() => navigation.navigate('CreateEvent')}
      />
    );
  }

  return (
    <FlatList
      data={data}
      renderItem={({ item }) => <EventCard event={item} />}
      refreshing={loading}
      onRefresh={refetch}
      onEndReached={fetchMore}
      ListFooterComponent={
        loading ? <LoadingMoreFooter /> : null
      }
    />
  );
}

Troubleshooting

States not animating

Ensure react-native-reanimated is installed and configured:

npx expo install react-native-reanimated

Add to babel.config.js:

plugins: [
  'react-native-reanimated/plugin', // Must be last
],

Confirmation dialogs not showing on iOS

Use showNativeConfirmation for iOS/Android instead of custom modal:

if (Platform.OS !== 'web') {
  showNativeConfirmation({...});
}

Empty states taking full screen

Add flex: 1 to parent container:

<View style={{ flex: 1 }}>
  <NoFriendsEmpty />
</View>

Resources