Load One Async Component in TanStack Start Without Blocking the Entire Page

Learn how to integrate Google Gemini API into your Spring Boot applications using Spring AI. Step-by-step guide with code examples, configuration, and best practices for building AI-powered Java applications.

Load One Async Component in TanStack Start Without Blocking the Entire Page

Modern web apps often mix fast and slow data fetches. You don’t want a single 300ms API call to make the whole page feel sluggish. In TanStack Start, you can achieve true partial loading using React Suspense and TanStack Query — so one component shows a loading state while everything else renders instantly.


The Problem

Imagine a dashboard page:

  • A statistics overview loads in 2ms
  • A heavy analytics chart loads in 300ms

If you fetch everything together, users see a full-page loading state until the slowest request completes — resulting in a poor user experience.


The Solution: Component-Level Suspense + useSuspenseQuery

TanStack Start (built on React) supports streaming SSR and fine-grained loading states. The key is to wrap only the slow component with a <Suspense> boundary.

1. Page / Route Component

// app/routes/dashboard.tsx
import { Suspense } from 'react';
import { FastStats } from './components/FastStats';
import { SlowAnalytics } from './components/SlowAnalytics';

export default function Dashboard() {
  return (
    <div className="space-y-8 p-6">
      <h1 className="text-3xl font-bold">Dashboard</h1>

      {/* Fast component renders immediately */}
      <FastStats />

      {/* Only this section waits */}
      <Suspense fallback={<AnalyticsSkeleton />}>
        <SlowAnalytics />
      </Suspense>

      {/* Other components continue rendering */}
      <QuickLinks />
    </div>
  );
}

2. The Slow Component (Using useSuspenseQuery)

// components/SlowAnalytics.tsx
import { useSuspenseQuery } from '@tanstack/react-query';

async function fetchAnalytics() {
  // Simulate real API delay
  await new Promise((resolve) => setTimeout(resolve, 300));
  
  const res = await fetch('/api/analytics');
  return res.json();
}

export function SlowAnalytics() {
  const { data } = useSuspenseQuery({
    queryKey: ['analytics'],
    queryFn: fetchAnalytics,
  });

  return (
    <div className="bg-white rounded-xl shadow p-6">
      <h2 className="text-xl font-semibold mb-4">Analytics Overview</h2>
      {/* Render your charts, tables, etc. */}
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

function AnalyticsSkeleton() {
  return (
    <div className="bg-white rounded-xl shadow p-6 animate-pulse">
      <div className="h-6 bg-gray-200 rounded w-1/3 mb-6"></div>
      <div className="space-y-4">
        <div className="h-40 bg-gray-100 rounded"></div>
      </div>
    </div>
  );
}

3. The Fast Component

// components/FastStats.tsx
import { useQuery } from '@tanstack/react-query';

export function FastStats() {
  const { data } = useQuery({
    queryKey: ['stats'],
    queryFn: async () => {
      await new Promise((r) => setTimeout(r, 2));
      return { users: 1243, revenue: 45820 };
    },
  });

  return (
    <div className="grid grid-cols-3 gap-4">
      {/* Stats cards */}
    </div>
  );
}

Why This Works So Well in TanStack Start

  • Streaming SSR: The server sends HTML for the fast parts immediately. The slow component streams in later.
  • useSuspenseQuery: Automatically suspends the component (throws a promise) until data is ready.
  • Independent Loading States: Only the wrapped component shows loading — the rest of the UI stays interactive.
  • Great for UX: Users can start reading and interacting while heavy sections load.

Pro Tips

  1. Place as close as possible to the async component for better granularity.
  2. Error Handling: Wrap with or use TanStack Router’s errorComponent.
  3. Multiple Slow Components:
<Suspense fallback={<SkeletonA />}>
  <ComponentA />
</Suspense>
<Suspense fallback={<SkeletonB />}>
  <ComponentB />
</Suspense>
  1. Server Components: You can also return promises directly from server components and use React.use() + Suspense.
  2. Prefetching: Use queryClient.prefetchQuery() in route loaders for even faster perceived performance.

Final Thoughts

TanStack Start + TanStack Query gives you one of the most powerful data-fetching experiences available in React today. By leveraging Suspense boundaries, you can stop treating your page as a single unit and start thinking in independent async components.

This pattern scales beautifully for complex dashboards, e-commerce product pages, user profiles — anywhere you have mixed-speed data requirements.

Member discussion

0 comments

Start the conversation

Become a member of >hacksubset_ to start commenting.