Skip to content

AsyncContent

Purpose

AsyncContent provides consistent handling of async data states:

  • Loading state with custom skeleton
  • Error state with retry option
  • Empty state with call-to-action
  • Success state with render function

When to use

Use AsyncContent for:

  • Any component that fetches data
  • Consistent loading/error/empty patterns
  • Wrapping React Query results

For full page layouts, prefer PageLayout which includes AsyncContent.

Basic usage

tsx
import { AsyncContent } from '@/shared/ui/components/AsyncContent'
import { FolderIcon, Plus } from 'lucide-react'

export function ProjectList() {
  const { data: projects, isLoading, error, refetch } = useProjects()

  return (
    <AsyncContent
      isLoading={isLoading}
      error={error}
      data={projects}
      loadingFallback={<ProjectsSkeleton />}
      errorConfig={{
        title: 'Failed to load projects',
        icon: FolderIcon,
        onRetry: refetch,
      }}
      emptyConfig={{
        title: 'No projects',
        description: 'Create your first project',
        action: { label: 'New Project', onClick: handleCreate, icon: Plus },
      }}
    >
      {(projects) => (
        <ul>
          {projects.map((p) => (
            <li key={p.id}>{p.name}</li>
          ))}
        </ul>
      )}
    </AsyncContent>
  )
}

Props

PropTypeRequiredDescription
isLoadingbooleanYesLoading state flag
errorError | nullNoError object
dataTYesThe data to render
isEmptyboolean | ((data: T) => boolean)NoCustom empty check
loadingFallbackReactNodeYesLoading skeleton
errorConfigErrorConfigNoError state configuration
emptyConfigEmptyConfigNoEmpty state configuration
children(data: T) => ReactNodeYesRender function for success
classNamestringNoContainer class

Empty detection

By default, AsyncContent checks if data is an empty array. For non-array data, provide isEmpty:

tsx
// Object data
<AsyncContent
  data={user}
  isEmpty={!user}
  // ...
>
  {(user) => <UserProfile user={user} />}
</AsyncContent>

// Custom check
<AsyncContent
  data={results}
  isEmpty={(data) => data.items.length === 0}
  // ...
>
  {(results) => <ResultsList results={results} />}
</AsyncContent>

State components

AsyncContent uses these internal components:

  • ErrorState — Displays error with optional retry
  • EmptyState — Displays empty message with optional action
  • LoadingState — Your custom loadingFallback

These are also exported from @/shared/ui/components/states for standalone use.

Notes

  • The render function children only executes when data is available
  • Error state shows error.message if no custom description provided
  • For page-level async handling, use PageLayout