Suspensive Documentation

website·Indexed 32 days ago

https://suspensive.org

A library providing async-safe React primitives and components. It includes @suspensive/react, which offers drop-in replacements and enhancements for React.Suspense and ErrorBoundary, as well as utilities like ClientOnly, DefaultPropsProvider, and the useIsClient hook. The documentation also covers @suspensive/react-query and its migration path to TanStack Query v4.

Tokens
18.8K
Snippets
108
Records
125
Agent score
66%

What's inside Suspensive

  1. Overview of Suspensive

    Suspensive is a library designed to fill gaps in React's built-in async rendering, error handling, and data fetching capabilities. It provides declarative components and hooks to simplify the implementation of Suspense boundaries, Error Boundaries, and lazy loading, while also offering specialized integrations for TanStack Query and Jotai to reduce boilerplate and improve SSR (Server-Side Rendering) behavior.
  2. Handle removed options in useSuspenseQuery and useSuspenseInfiniteQuery

    In recent versions, the enabled and placeholderData options have been removed from useSuspenseQuery and useSuspenseInfiniteQuery. These options are incompatible with the Suspense architecture.
    const query = useSuspenseQuery({
      queryKey: ['key'],
      queryFn: () => api.text()
    - enabled: Math.random() > 0.5,
    - placeholderData: 'placeholder'
    })
  3. Use @suspensive/react Suspense as a replacement for React.Suspense

    @suspensive/react's <Suspense/> is a drop-in replacement for React's native <Suspense> component. It maintains the same basic suspense boundary functionality but adds support for skipping server-side rendering (SSR), global default fallbacks via DefaultPropsProvider, and a Higher-Order Component (HOC) pattern via Suspense.with.
    import { Suspense } from '@suspensive/react'
    
    const Example = () => (
      <Suspense fallback={<Loading />}>
        <Children />
      </Suspense>
    )
  4. Migrate from react-error-boundary to @suspensive/react

    Migrating from react-error-boundary involves updating the fallback prop name and the reset function name. fallbackRender or FallbackComponent becomes fallback, and resetErrorBoundary becomes reset.
    // From react-error-boundary
    <ErrorBoundary
      fallbackRender={({ error, resetErrorBoundary }) => <div onClick={resetErrorBoundary}>{error.message}</div>}
      onReset={() => console.log('reset')}
    >
      <YourComponent />
    </ErrorBoundary>
    
    // To @suspensive/react
    <ErrorBoundary
      fallback={({ error, reset }) => <div onClick={reset}>{error.message}</div>}
      onReset={() => console.log('reset')}
    >
      <YourComponent />
    </ErrorBoundary>
  5. Prevent loading state flicker with the Delay component

    The <Delay/> component prevents 'flash-of-loading-state' by holding back the loading UI until a specified time (ms) has passed. This is particularly useful when used as a fallback for <Suspense/> to hide spinners on fast networks.

    Recommended ms values:

    • 100–300ms: Standard UI to hide spinners on fast networks.
    • 500ms+: Non-critical sections where delayed feedback is acceptable.
    <Suspense
      fallback={
        <Delay ms={200}>
          {({ isDelayed }) => (
            <Spinner
              style={{ opacity: isDelayed ? 1 : 0, transition: 'opacity 200ms' }}
            />
          )}
        </Delay>
      }
    >
      <Content />
    </Suspense>
  6. Use queryOptions for reusable query configurations

    The queryOptions pattern allows you to define query configurations (like queryKey and queryFn) in a reusable way. These options can be passed directly into various hooks (useQuery, useSuspenseQuery, useQueries, useSuspenseQueries) and the <SuspenseQuery /> component, as well as queryClient methods.
    // Define reusable options
    const postQueryOptions = (id: number) => queryOptions({
      queryKey: ['posts', id],
      queryFn: () => fetchPost(id),
    });
    
    // Use in hooks
    const { data: post1 } = useSuspenseQuery({
       ...postQueryOptions(1),
       refetchInterval: 2000,
    });
    
    // Use in components
    const Example = () => (
      <SuspenseQuery {...postQueryOptions(1)}>
        {({ data: post1 }) => <>{post1.text}</>}
      </SuspenseQuery>
    );
    
    // Use with QueryClient
    const queryClient = useQueryClient();
    queryClient.prefetchQuery(postQueryOptions(1));
    queryClient.invalidateQueries(postQueryOptions(1));
  7. Prefetch and hydrate multiple queries in Server Components using QueriesHydration

    The <QueriesHydration /> component simplifies the process of prefetching data on the server and hydrating it on the client in React Server Components (RSC) environments. It replaces the manual process of creating a QueryClient, calling ensureQueryData, and wrapping components in a HydrationBoundary.

    Key benefits include:

    • Concise code: Automates the boilerplate of QueryClient creation and dehydration.
    • Parallel fetching: Uses Promise.all to fetch all provided queries in parallel.
    • Type safety: Requires queryKey to ensure consistency.
    • Timeout support: Supports a timeout prop to throw an error if queries take too long to fetch.
    import { QueriesHydration } from '@suspensive/react-query'
    import { Suspense } from '@suspensive/react'
    import { userQueryOptions } from './queries'
    import { UserProfile } from './_components'
    
    // Server Component
    const Page = async ({ userId }: { userId: number }) => {
      return (
        <Suspense fallback={<div>Loading...</div>}>
          {/* Prefetches userQueryOptions and hydrates UserProfile on the client */}
          <QueriesHydration queries={[userQueryOptions(userId)]} timeout={1000}>
            <UserProfile userId={userId} />
          </QueriesHydration>
        </Suspense>
      )
    }
  8. Migrate useSuspenseQueries to TanStack Query

    The useSuspenseQueries hook in @suspensive/react-query is deprecated. This functionality is officially supported in TanStack Query v4.40.0+. Developers should migrate by changing the import source from @suspensive/react-query to @tanstack/react-query.
    - import { useSuspenseQueries } from '@suspensive/react-query'
    + import { useSuspenseQueries } from '@tanstack/react-query'
  9. Migrate to Suspensive v2

    Suspensive v2 focuses on React 18 compatibility and developer experience.

    Critical Requirement: React 18.0 or later is required because the library now uses the native useSyncExternalStore hook.

    Key Changes and Features:

    • Native React 18 APIs: Updated to leverage native Suspense-related features.
    • queryOptions: Introduced for improved type safety and query management.
    • clientOnly prop: Added to Suspense as a replacement for Suspense.CSROnly.
    • DevMode: Added for enhanced debugging.
    • Naming Conventions: Aligned with TanStack Query v5.
    • Deprecations: AsyncBoundary is deprecated in favor of a cleaner API design.
  10. Install @suspensive/jotai

    Install the latest stable version of @suspensive/jotai via npm. The library requires React ^18 or ^19 and Jotai ^2. It is compatible with modern browsers including Chrome >= 51, Firefox >= 53, Edge >= 18, Safari >= 11, iOS >= 11, and Opera >= 38.
    npm install @suspensive/jotai
  11. Perform declarative data fetching with SuspenseQuery

    Using @suspensive/react-query, you can fetch data as JSX using the <SuspenseQuery> component. This removes the need for useSuspenseQuery hooks or splitting components to handle data fetching at the same depth as the UI.
    <ErrorBoundary fallback={<ErrorUI />}>
      <Suspense fallback={<Skeleton />}>
        <SuspenseQuery {...userQueryOptions()}>
          {({ data: user }) => <UserProfile user={user} />}
        </SuspenseQuery>
      </Suspense>
    </ErrorBoundary>
  12. Migrate from React Class Component Error Boundaries

    Replace complex class-based error boundaries (using getDerivedStateFromError and componentDidCatch) with the declarative <ErrorBoundary/> component. Use the onError prop to replace componentDidCatch logic.
    import { ErrorBoundary } from '@suspensive/react'
    
    const SuspensiveExample = () => (
      <ErrorBoundary
        fallback={({ error, reset }) => <FallbackComponent error={error} reset={reset} />}
        onError={(error, errorInfo) => logErrorToService(error, errorInfo)}
      >
        <YourComponent />
      </ErrorBoundary>
    )