0
# Control Flow
1
2
Built-in control flow components for conditional rendering, list rendering, and error boundaries with optimized updates and proper cleanup.
3
4
## Capabilities
5
6
### Conditional Rendering
7
8
Render content conditionally based on boolean or truthy values with automatic cleanup.
9
10
```typescript { .api }
11
/**
12
* Conditionally render its children or an optional fallback component
13
* @param props - Show component props
14
* @returns JSX element based on condition
15
*/
16
function Show<T>(props: {
17
when: T | undefined | null | false;
18
keyed?: boolean;
19
fallback?: JSX.Element;
20
children: JSX.Element | ((item: NonNullable<T> | Accessor<NonNullable<T>>) => JSX.Element);
21
}): JSX.Element;
22
```
23
24
**Usage Examples:**
25
26
```typescript
27
import { Show, createSignal } from "solid-js";
28
29
function ConditionalContent() {
30
const [user, setUser] = createSignal<{ name: string; email: string } | null>(null);
31
const [loading, setLoading] = createSignal(false);
32
33
// Basic conditional rendering
34
return (
35
<div>
36
<Show when={!loading()} fallback={<div>Loading...</div>}>
37
<div>Content loaded!</div>
38
</Show>
39
40
{/* Conditional rendering with data access */}
41
<Show when={user()} fallback={<div>No user logged in</div>}>
42
{(userData) => (
43
<div>
44
<h1>Welcome, {userData.name}!</h1>
45
<p>Email: {userData.email}</p>
46
</div>
47
)}
48
</Show>
49
50
{/* Nested conditional rendering */}
51
<Show when={user()}>
52
<Show when={user()?.email} fallback={<p>No email provided</p>}>
53
<p>Contact: {user()!.email}</p>
54
</Show>
55
</Show>
56
</div>
57
);
58
}
59
```
60
61
### Multiple Conditions
62
63
Handle mutually exclusive conditions with Switch and Match components.
64
65
```typescript { .api }
66
/**
67
* Switches between content based on mutually exclusive conditions
68
* @param props - Switch component props
69
* @returns JSX element based on first matching condition
70
*/
71
function Switch(props: {
72
fallback?: JSX.Element;
73
children: JSX.Element;
74
}): JSX.Element;
75
76
/**
77
* Selects content based on condition when inside a Switch control flow
78
* @param props - Match component props
79
* @returns JSX element if condition matches
80
*/
81
function Match<T>(props: {
82
when: T | undefined | null | false;
83
keyed?: boolean;
84
children: JSX.Element | ((item: NonNullable<T> | Accessor<NonNullable<T>>) => JSX.Element);
85
}): JSX.Element;
86
```
87
88
**Usage Examples:**
89
90
```typescript
91
import { Switch, Match, createSignal } from "solid-js";
92
93
function StatusDisplay() {
94
const [status, setStatus] = createSignal<"loading" | "success" | "error" | "idle">("idle");
95
const [data, setData] = createSignal<any>(null);
96
const [error, setError] = createSignal<string | null>(null);
97
98
return (
99
<div>
100
<Switch fallback={<div>Unknown status</div>}>
101
<Match when={status() === "loading"}>
102
<div class="spinner">Loading...</div>
103
</Match>
104
105
<Match when={status() === "success" && data()}>
106
{(successData) => (
107
<div class="success">
108
<h2>Success!</h2>
109
<pre>{JSON.stringify(successData, null, 2)}</pre>
110
</div>
111
)}
112
</Match>
113
114
<Match when={status() === "error" && error()}>
115
{(errorMessage) => (
116
<div class="error">
117
<h2>Error occurred</h2>
118
<p>{errorMessage}</p>
119
<button onClick={() => setStatus("idle")}>Retry</button>
120
</div>
121
)}
122
</Match>
123
124
<Match when={status() === "idle"}>
125
<div>
126
<p>Ready to start</p>
127
<button onClick={() => setStatus("loading")}>Load Data</button>
128
</div>
129
</Match>
130
</Switch>
131
</div>
132
);
133
}
134
```
135
136
### List Rendering
137
138
Render lists of items with efficient keying and reactivity.
139
140
```typescript { .api }
141
/**
142
* Creates a list of elements from a list with item-based keying
143
* @param props - For component props
144
* @returns Array of JSX elements
145
*/
146
function For<T extends readonly any[], U extends JSX.Element>(props: {
147
each: T | undefined | null | false;
148
children: (item: T[number], index: Accessor<number>) => U;
149
fallback?: JSX.Element;
150
}): JSX.Element;
151
152
/**
153
* Non-keyed iteration over a list creating elements from its items
154
* @param props - Index component props
155
* @returns Array of JSX elements
156
*/
157
function Index<T extends readonly any[], U extends JSX.Element>(props: {
158
each: T | undefined | null | false;
159
children: (item: Accessor<T[number]>, index: number) => U;
160
fallback?: JSX.Element;
161
}): JSX.Element;
162
```
163
164
**Usage Examples:**
165
166
```typescript
167
import { For, Index, createSignal } from "solid-js";
168
169
function ListExamples() {
170
const [users, setUsers] = createSignal([
171
{ id: 1, name: "John", age: 30 },
172
{ id: 2, name: "Jane", age: 25 },
173
{ id: 3, name: "Bob", age: 35 }
174
]);
175
176
const [numbers, setNumbers] = createSignal([1, 2, 3, 4, 5]);
177
178
return (
179
<div>
180
{/* For component - item-based keying (recommended for object arrays) */}
181
<h2>Users (For)</h2>
182
<ul>
183
<For each={users()} fallback={<li>No users found</li>}>
184
{(user, index) => (
185
<li>
186
<strong>{index() + 1}.</strong> {user.name} (Age: {user.age})
187
<button onClick={() => {
188
setUsers(prev => prev.filter(u => u.id !== user.id));
189
}}>
190
Remove
191
</button>
192
</li>
193
)}
194
</For>
195
</ul>
196
197
{/* Index component - index-based keying (better for simple arrays) */}
198
<h2>Numbers (Index)</h2>
199
<ul>
200
<Index each={numbers()} fallback={<li>No numbers</li>}>
201
{(number, index) => (
202
<li>
203
Position {index}: {number()}
204
<button onClick={() => {
205
setNumbers(prev => prev.filter((_, i) => i !== index));
206
}}>
207
Remove
208
</button>
209
</li>
210
)}
211
</Index>
212
</ul>
213
214
{/* Dynamic list updates */}
215
<div>
216
<button onClick={() => {
217
setUsers(prev => [...prev, {
218
id: Date.now(),
219
name: `User ${prev.length + 1}`,
220
age: Math.floor(Math.random() * 50) + 20
221
}]);
222
}}>
223
Add User
224
</button>
225
226
<button onClick={() => {
227
setNumbers(prev => [...prev, prev.length + 1]);
228
}}>
229
Add Number
230
</button>
231
</div>
232
</div>
233
);
234
}
235
```
236
237
### Error Boundaries
238
239
Catch and handle errors in component trees with recovery mechanisms.
240
241
```typescript { .api }
242
/**
243
* Catches uncaught errors inside components and renders a fallback content
244
* @param props - ErrorBoundary component props
245
* @returns JSX element with error handling
246
*/
247
function ErrorBoundary(props: {
248
fallback: JSX.Element | ((err: any, reset: () => void) => JSX.Element);
249
children: JSX.Element;
250
}): JSX.Element;
251
252
/**
253
* Resets all error boundaries
254
*/
255
function resetErrorBoundaries(): void;
256
```
257
258
**Usage Examples:**
259
260
```typescript
261
import { ErrorBoundary, resetErrorBoundaries, createSignal } from "solid-js";
262
263
// Error boundary with component fallback
264
function ErrorFallback(props: { err: Error; reset: () => void }) {
265
return (
266
<div class="error-boundary">
267
<h2>Something went wrong</h2>
268
<details>
269
<summary>Error details</summary>
270
<pre>{props.err.message}</pre>
271
<pre>{props.err.stack}</pre>
272
</details>
273
<div>
274
<button onClick={props.reset}>Try again</button>
275
<button onClick={() => resetErrorBoundaries()}>Reset all</button>
276
</div>
277
</div>
278
);
279
}
280
281
// Component that might throw an error
282
function RiskyComponent() {
283
const [shouldError, setShouldError] = createSignal(false);
284
285
if (shouldError()) {
286
throw new Error("Something went wrong in RiskyComponent!");
287
}
288
289
return (
290
<div>
291
<p>This component works fine</p>
292
<button onClick={() => setShouldError(true)}>
293
Trigger Error
294
</button>
295
</div>
296
);
297
}
298
299
// App with error boundary
300
function App() {
301
return (
302
<div>
303
<h1>My App</h1>
304
305
<ErrorBoundary fallback={ErrorFallback}>
306
<RiskyComponent />
307
<div>Other content that should still render</div>
308
</ErrorBoundary>
309
310
{/* Error boundary with inline fallback */}
311
<ErrorBoundary
312
fallback={(err, reset) => (
313
<div class="inline-error">
314
<p>Error: {err.message}</p>
315
<button onClick={reset}>Retry</button>
316
</div>
317
)}
318
>
319
<AnotherRiskyComponent />
320
</ErrorBoundary>
321
</div>
322
);
323
}
324
```
325
326
### Advanced Suspense Control
327
328
Control the order and rendering behavior of suspended content.
329
330
```typescript { .api }
331
/**
332
* Controls the order in which suspended content is revealed (experimental)
333
* @param props - SuspenseList component props
334
* @returns JSX element with controlled suspense ordering
335
*/
336
function SuspenseList(props: {
337
children: JSX.Element;
338
revealOrder: "forwards" | "backwards" | "together";
339
tail?: "collapsed" | "hidden";
340
}): JSX.Element;
341
```
342
343
**Usage Examples:**
344
345
```typescript
346
import { SuspenseList, Suspense, For, createResource } from "solid-js";
347
348
function SequentialSuspenseExample() {
349
const [posts] = createResource(() => fetchPosts());
350
351
return (
352
<SuspenseList revealOrder="forwards" tail="collapsed">
353
<For each={posts()}>
354
{(post) => (
355
<Suspense fallback={<div>Loading post...</div>}>
356
<PostCard postId={post.id} />
357
</Suspense>
358
)}
359
</For>
360
</SuspenseList>
361
);
362
}
363
364
// Posts will be revealed in order, waiting for previous ones to complete
365
function PostCard(props: { postId: number }) {
366
const [postDetails] = createResource(() => fetchPostDetails(props.postId));
367
368
return (
369
<div class="post-card">
370
<h3>{postDetails()?.title}</h3>
371
<p>{postDetails()?.content}</p>
372
</div>
373
);
374
}
375
```
376
377
### Suspense Integration
378
379
Control flow components work seamlessly with Suspense for async operations.
380
381
**Usage Examples:**
382
383
```typescript
384
import { For, Show, ErrorBoundary, Suspense, createResource } from "solid-js";
385
386
function AsyncListExample() {
387
const [users] = createResource(() => fetchUsers());
388
389
return (
390
<div>
391
<ErrorBoundary fallback={(err, reset) => (
392
<div>
393
<p>Failed to load users: {err.message}</p>
394
<button onClick={reset}>Retry</button>
395
</div>
396
)}>
397
<Suspense fallback={<div>Loading users...</div>}>
398
<Show when={users()} fallback={<div>No users available</div>}>
399
<For each={users()}>
400
{(user) => (
401
<div class="user-card">
402
<h3>{user.name}</h3>
403
<p>{user.email}</p>
404
</div>
405
)}
406
</For>
407
</Show>
408
</Suspense>
409
</ErrorBoundary>
410
</div>
411
);
412
}
413
414
async function fetchUsers() {
415
const response = await fetch('/api/users');
416
if (!response.ok) {
417
throw new Error('Failed to fetch users');
418
}
419
return response.json();
420
}
421
```
422
423
## Performance Considerations
424
425
Control flow components in SolidJS are optimized for performance:
426
427
- **Fine-grained updates**: Only the specific items that change are updated
428
- **Keyed reconciliation**: For components use item identity for efficient updates
429
- **Lazy evaluation**: Content is only created when conditions are met
430
- **Automatic cleanup**: Resources are properly disposed when conditions change
431
432
```typescript
433
// Example of performance optimization
434
function OptimizedList() {
435
const [items, setItems] = createSignal([
436
{ id: 1, name: "Item 1", visible: true },
437
{ id: 2, name: "Item 2", visible: false },
438
{ id: 3, name: "Item 3", visible: true }
439
]);
440
441
// Only visible items are rendered
442
const visibleItems = createMemo(() =>
443
items().filter(item => item.visible)
444
);
445
446
return (
447
<For each={visibleItems()}>
448
{(item) => (
449
<div class="item">
450
{item.name}
451
<button
452
onClick={() => {
453
setItems(prev => prev.map(i =>
454
i.id === item.id
455
? { ...i, visible: !i.visible }
456
: i
457
));
458
}}
459
>
460
Toggle
461
</button>
462
</div>
463
)}
464
</For>
465
);
466
}
467
```