> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/preactjs/preact/llms.txt
> Use this file to discover all available pages before exploring further.

# Suspense

> Suspend rendering while loading async data with Suspense and lazy in Preact

# Suspense

Suspend rendering while loading asynchronous data or code. Display a fallback UI until the suspended content is ready.

## Suspense Component

### Signature

```typescript theme={null}
interface SuspenseProps {
  children?: ComponentChildren;
  fallback?: ComponentChildren;
}

class Suspense extends Component<SuspenseProps> {}
```

<ParamField path="children" type="ComponentChildren">
  The content to render once data is loaded.
</ParamField>

<ParamField path="fallback" type="ComponentChildren">
  The fallback UI to show while suspended (typically a loading indicator).
</ParamField>

### Usage

```jsx theme={null}
import { Suspense } from 'preact/compat';

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <UserProfile />
    </Suspense>
  );
}
```

## lazy

Lazy load components for code splitting.

### Signature

```typescript theme={null}
function lazy<T extends ComponentType<any>>(
  loader: () => Promise<{ default: T }>
): T
```

<ParamField path="loader" type="() => Promise<{ default: T }>" required>
  A function that returns a Promise resolving to a module with a `default` export containing the component.
</ParamField>

<ParamField path="returns" type="T">
  A lazy-loaded component that can be rendered like a normal component.
</ParamField>

### Usage

```jsx theme={null}
import { Suspense, lazy } from 'preact/compat';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading component...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}
```

## Complete Examples

### Lazy Loading Routes

```jsx theme={null}
import { Suspense, lazy } from 'preact/compat';
import { Router, Route } from 'preact-router';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const Dashboard = lazy(() => import('./routes/Dashboard'));

function App() {
  return (
    <Suspense fallback={<div className="loader">Loading...</div>}>
      <Router>
        <Route path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route path="/dashboard" component={Dashboard} />
      </Router>
    </Suspense>
  );
}
```

### Multiple Lazy Components

```jsx theme={null}
import { Suspense, lazy } from 'preact/compat';

const Sidebar = lazy(() => import('./Sidebar'));
const Content = lazy(() => import('./Content'));

function Dashboard() {
  return (
    <div className="dashboard">
      <Suspense fallback={<div>Loading sidebar...</div>}>
        <Sidebar />
      </Suspense>
      
      <Suspense fallback={<div>Loading content...</div>}>
        <Content />
      </Suspense>
    </div>
  );
}
```

### Nested Suspense

```jsx theme={null}
import { Suspense, lazy } from 'preact/compat';

const Comments = lazy(() => import('./Comments'));
const UserProfile = lazy(() => import('./UserProfile'));

function Post() {
  return (
    <Suspense fallback={<PageLoader />}>
      <article>
        <h1>Blog Post</h1>
        
        <Suspense fallback={<ProfileSkeleton />}>
          <UserProfile />
        </Suspense>
        
        <p>Post content...</p>
        
        <Suspense fallback={<CommentsSkeleton />}>
          <Comments />
        </Suspense>
      </article>
    </Suspense>
  );
}
```

### Data Fetching with Suspense

```jsx theme={null}
import { Suspense } from 'preact/compat';

// Create a resource that suspends
function fetchUser(userId) {
  let status = 'pending';
  let result;
  
  const promise = fetch(`/api/users/${userId}`)
    .then(res => res.json())
    .then(data => {
      status = 'success';
      result = data;
    })
    .catch(error => {
      status = 'error';
      result = error;
    });
  
  return {
    read() {
      if (status === 'pending') throw promise;
      if (status === 'error') throw result;
      return result;
    }
  };
}

const userResource = fetchUser(1);

function UserProfile() {
  const user = userResource.read();
  
  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

function App() {
  return (
    <Suspense fallback={<div>Loading user...</div>}>
      <UserProfile />
    </Suspense>
  );
}
```

## Implementation Details

### Suspense Component

The `Suspense` implementation:

```javascript theme={null}
export function Suspense() {
  this._pendingSuspensionCount = 0;
  this._suspenders = null;
  this._detachOnNextRender = null;
}

Suspense.prototype = new Component();

Suspense.prototype.render = function (props, state) {
  if (this._detachOnNextRender) {
    // Handle detaching suspended content
    if (this._vnode._children) {
      const detachedParent = document.createElement('div');
      const detachedComponent = this._vnode._children[0]._component;
      this._vnode._children[0] = detachedClone(
        this._detachOnNextRender,
        detachedParent,
        (detachedComponent._originalParentDom = detachedComponent._parentDom)
      );
    }
    this._detachOnNextRender = null;
  }

  return [
    createElement(Fragment, null, state._suspended ? null : props.children),
    state._suspended && createElement(Fragment, null, props.fallback)
  ];
};
```

### lazy Function

The `lazy` implementation:

```javascript theme={null}
export function lazy(loader) {
  let prom;
  let component = null;
  let error;
  let resolved;

  function Lazy(props) {
    if (!prom) {
      prom = loader();
      prom.then(
        exports => {
          if (exports) {
            component = exports.default || exports;
          }
          resolved = true;
        },
        e => {
          error = e;
          resolved = true;
        }
      );
    }

    if (error) {
      throw error;
    }

    if (!resolved) {
      throw prom;
    }

    return component ? createElement(component, props) : null;
  }

  Lazy.displayName = 'Lazy';
  return Lazy;
}
```

### How Suspense Works

1. **Promise Thrown**: A component throws a Promise when data isn't ready
2. **Suspense Catches**: Nearest `Suspense` boundary catches the Promise
3. **Fallback Shown**: `Suspense` renders the `fallback` prop
4. **Promise Resolves**: When the Promise resolves, the component re-renders
5. **Content Displayed**: Real content replaces the fallback

## Error Boundaries with Suspense

Combine with error boundaries for error handling:

```jsx theme={null}
import { Component, Suspense, lazy } from 'preact/compat';

class ErrorBoundary extends Component {
  state = { hasError: false };
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, errorInfo) {
    console.error('Error:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return <div>Something went wrong.</div>;
    }
    return this.props.children;
  }
}

const LazyComponent = lazy(() => import('./Component'));

function App() {
  return (
    <ErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <LazyComponent />
      </Suspense>
    </ErrorBoundary>
  );
}
```

## Fallback Components

### Simple Loader

```jsx theme={null}
function Loader() {
  return (
    <div className="loader">
      <div className="spinner" />
      <p>Loading...</p>
    </div>
  );
}

<Suspense fallback={<Loader />}>
  <Content />
</Suspense>
```

### Skeleton Screen

```jsx theme={null}
function UserSkeleton() {
  return (
    <div className="skeleton">
      <div className="skeleton-avatar" />
      <div className="skeleton-line" />
      <div className="skeleton-line short" />
    </div>
  );
}

<Suspense fallback={<UserSkeleton />}>
  <UserProfile />
</Suspense>
```

## Best Practices

1. **Granular Suspense**: Use multiple `Suspense` boundaries for better UX
2. **Meaningful Fallbacks**: Show skeleton screens instead of generic spinners
3. **Error Boundaries**: Always wrap `Suspense` in error boundaries
4. **Loading States**: Consider showing partial content while loading
5. **Code Splitting**: Use `lazy` for route-based code splitting

## Transition to Loaded Content

Use CSS for smooth transitions:

```css theme={null}
.content {
  animation: fadeIn 0.3s ease-in;
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
```

```jsx theme={null}
<Suspense fallback={<Loader />}>
  <div className="content">
    <UserProfile />
  </div>
</Suspense>
```

## useTransition Hook

For smoother transitions between loading states:

```jsx theme={null}
import { useState, useTransition, Suspense, lazy } from 'preact/compat';

const Tab1 = lazy(() => import('./Tab1'));
const Tab2 = lazy(() => import('./Tab2'));

function Tabs() {
  const [tab, setTab] = useState('tab1');
  const [isPending, startTransition] = useTransition();
  
  const switchTab = (newTab) => {
    startTransition(() => {
      setTab(newTab);
    });
  };
  
  return (
    <>
      <button onClick={() => switchTab('tab1')}>Tab 1</button>
      <button onClick={() => switchTab('tab2')}>Tab 2</button>
      
      {isPending && <div>Loading tab...</div>}
      
      <Suspense fallback={<div>Loading...</div>}>
        {tab === 'tab1' ? <Tab1 /> : <Tab2 />}
      </Suspense>
    </>
  );
}
```

## Source

Implementation: compat/src/suspense.js:1-255
