or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

component-system.mdcontext-scoping.mdcontrol-flow.mdindex.mdreactive-primitives.mdresources-async.mdstore-management.mdweb-rendering.md

control-flow.mddocs/

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

```