Skip to content

Commit e9b7233

Browse files
Remove Suspense, simplify loading experience
- AvatarCall now handles all loading states internally - No need for <Suspense> wrappers - just render AvatarCall - useCredentials uses regular state instead of throwing promises - Added LoadingSessionProvider for seamless context during credential fetch - Added default loading spinner to AvatarVideo - onError callback triggered imperatively instead of via useEffect - Removed suspense-resource.ts utility - Updated all documentation to reflect new API Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent e1e013f commit e9b7233

17 files changed

Lines changed: 524 additions & 435 deletions

CHANGELOG.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.6.0] - 2026-02-10
11+
12+
### Changed
13+
14+
- **Breaking:** `AvatarCall` no longer uses React Suspense. Loading states are now handled internally for a seamless experience. Remove any `<Suspense>` wrappers around `AvatarCall`.
15+
16+
### Removed
17+
18+
- Removed Suspense-based credential fetching from `AvatarCall` — loading is now handled internally
19+
- Removed `suspense-resource.ts` utility (no longer needed)
20+
21+
### Fixed
22+
23+
- Fixed "Requested device not found" error when connecting or toggling mic/camera on devices without those peripherals
24+
- Fixed awkward loading state gap between Suspense resolution and video becoming ready
25+
- Added default loading spinner to AvatarVideo during connecting/waiting states
26+
- Fixed loading spinner positioning — now properly centered with absolute positioning
27+
- Fixed hooks (`useLocalMedia`, `useAvatar`) crashing when called outside LiveKitRoom context during credential loading
28+
1029
## [0.5.0] - 2026-02-10
1130

1231
### Changed
@@ -30,7 +49,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3049
- Added missing `data-avatar-enabled` attribute to screen-share and end-call control buttons
3150
- Added missing `toggleScreenShare` function to ControlBar render prop state
3251
- Blurred background now fades out smoothly when video becomes ready
33-
- Fixed "Requested device not found" error when connecting or toggling mic/camera on devices without those peripherals
3452

3553
## [0.4.0] - 2026-02-09
3654

docs/api/components.md

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
High-level component that handles the complete session lifecycle. **Recommended for most use cases.**
2020

21-
**Suspends during credential fetching** — wrap in `<Suspense>` to show loading UI while the server creates the session. See the [Loading States guide](../guides/loading-states.md) for details.
21+
Handles credential fetching and loading states internally — just render it and the SDK manages the rest. See the [Loading States guide](../guides/loading-states.md) for details.
2222

2323
### Props
2424

@@ -30,60 +30,55 @@ High-level component that handles the complete session lifecycle. **Recommended
3030
| `sessionId` | `string` |* | Session ID (use with `sessionKey`) |
3131
| `sessionKey` | `string` |* | Session key (use with `sessionId`) |
3232
| `credentials` | `SessionCredentials` |* | Pre-fetched credentials |
33-
| `baseUrl` | `string` | | Runway API base URL (defaults to `https://api.dev.runwayml.com`) |
34-
| `avatarImageUrl` | `string` | | Avatar image URL (available as `--avatar-image` CSS variable) |
33+
| `baseUrl` | `string` | | Runway API base URL (defaults to `https://api.runwayml.com`) |
34+
| `avatarImageUrl` | `string` | | Avatar image URL for loading placeholder (available as `--avatar-image` CSS variable) |
3535
| `className` | `string` | | CSS class name |
3636
| `style` | `CSSProperties` | | Inline styles |
3737
| `onEnd` | `() => void` | | Called when session ends |
38-
| `onError` | `(error: Error) => void` | | Called on WebRTC error |
38+
| `onError` | `(error: Error) => void` | | Called on credential or WebRTC error |
3939
| `children` | `ReactNode` | | Custom layout (defaults to AvatarVideo + UserVideo + ControlBar) |
4040

4141
*One of these is required: `connectUrl`, `connect`, `sessionId`+`sessionKey`, or `credentials`.
4242

4343
### Basic Usage
4444

4545
```tsx
46-
<Suspense fallback={<p>Connecting to avatar...</p>}>
47-
<AvatarCall
48-
avatarId="game-host"
49-
connectUrl="/api/avatar/connect"
50-
/>
51-
</Suspense>
46+
<AvatarCall
47+
avatarId="game-host"
48+
connectUrl="/api/avatar/connect"
49+
avatarImageUrl="/avatars/game-host.png"
50+
/>
5251
```
5352

5453
### Custom Connect Function
5554

5655
```tsx
57-
<Suspense fallback={<LoadingScreen />}>
58-
<AvatarCall
59-
avatarId="game-host"
60-
connect={async (avatarId) => {
61-
const res = await fetch('/api/avatar/connect', {
62-
method: 'POST',
63-
headers: {
64-
'Authorization': `Bearer ${token}`,
65-
'Content-Type': 'application/json',
66-
},
67-
body: JSON.stringify({ avatarId }),
68-
});
69-
return res.json();
70-
}}
71-
/>
72-
</Suspense>
56+
<AvatarCall
57+
avatarId="game-host"
58+
connect={async (avatarId) => {
59+
const res = await fetch('/api/avatar/connect', {
60+
method: 'POST',
61+
headers: {
62+
'Authorization': `Bearer ${token}`,
63+
'Content-Type': 'application/json',
64+
},
65+
body: JSON.stringify({ avatarId }),
66+
});
67+
return res.json();
68+
}}
69+
/>
7370
```
7471

7572
### Custom Layout
7673

7774
```tsx
78-
<Suspense fallback={<LoadingScreen />}>
79-
<AvatarCall avatarId="game-host" connectUrl="/api/avatar/connect">
80-
<div className="my-layout">
81-
<AvatarVideo className="main-video" />
82-
<UserVideo className="pip" />
83-
<ControlBar className="bottom-bar" />
84-
</div>
85-
</AvatarCall>
86-
</Suspense>
75+
<AvatarCall avatarId="game-host" connectUrl="/api/avatar/connect">
76+
<div className="my-layout">
77+
<AvatarVideo className="main-video" />
78+
<UserVideo className="pip" />
79+
<ControlBar className="bottom-bar" />
80+
</div>
81+
</AvatarCall>
8782
```
8883

8984
### Data Attributes

docs/api/types.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ interface SessionCredentials {
3737

3838
### AvatarCallProps
3939

40-
Suspends during credential fetching — wrap in `<Suspense>` to show loading UI.
40+
Handles credential fetching and loading states internally.
4141

4242
```typescript
4343
interface AvatarCallProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onError'> {

docs/getting-started.md

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,15 @@ Get your API secret from the [Runway Developer Portal](https://dev.runwayml.com/
2727
### Client Component
2828

2929
```tsx
30-
import { Suspense } from 'react';
3130
import { AvatarCall } from '@runwayml/avatars-react';
3231
import '@runwayml/avatars-react/styles.css'; // Optional default styles
3332

3433
function App() {
3534
return (
36-
<Suspense fallback={<p>Setting up avatar session...</p>}>
37-
<AvatarCall
38-
avatarId="game-host"
39-
connectUrl="/api/avatar/connect"
40-
/>
41-
</Suspense>
35+
<AvatarCall
36+
avatarId="game-host"
37+
connectUrl="/api/avatar/connect"
38+
/>
4239
);
4340
}
4441
```
@@ -102,7 +99,7 @@ export async function POST(req: Request) {
10299
4. `AvatarCall` establishes a real-time video connection (~1–5 seconds)
103100
5. User sees and talks to the AI avatar
104101

105-
> **Note:** Session creation (step 2) can take 10–30 seconds. `AvatarCall` **suspends** during this waitwrap it in `<Suspense>` to show a loading fallback. See the [Loading States guide](guides/loading-states.md) for details.
102+
> **Note:** Session creation (step 2) can take 10–30 seconds. `AvatarCall` handles loading states internallyuse `avatarImageUrl` and the default styles for a polished loading experience. See the [Loading States guide](guides/loading-states.md) for details.
106103
107104
## Session States
108105

0 commit comments

Comments
 (0)