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

web-rendering.mddocs/

0

# Web Rendering

1

2

DOM rendering utilities, web components, and hydration functions for building web applications with server-side rendering support.

3

4

## Capabilities

5

6

### DOM Rendering

7

8

Render SolidJS applications to the DOM with efficient updates and lifecycle management.

9

10

```typescript { .api }

11

/**

12

* Renders a reactive component tree to a DOM element

13

* @param code - Function that returns JSX to render

14

* @param element - DOM element to render into

15

* @returns Dispose function to cleanup the rendering

16

*/

17

function render(

18

code: () => JSX.Element,

19

element: MountableElement

20

): () => void;

21

22

/**

23

* Hydrates server-rendered content with client-side reactivity

24

* @param fn - Function that returns JSX to hydrate

25

* @param node - DOM element containing server-rendered content

26

* @returns Dispose function to cleanup hydration

27

*/

28

function hydrate(

29

fn: () => JSX.Element,

30

node: MountableElement

31

): () => void;

32

33

type MountableElement = Element | Document | ShadowRoot | DocumentFragment | Node;

34

```

35

36

**Usage Examples:**

37

38

```typescript

39

import { render, hydrate } from "solid-js/web";

40

import { createSignal } from "solid-js";

41

42

// Basic rendering

43

function App() {

44

const [count, setCount] = createSignal(0);

45

46

return (

47

<div>

48

<h1>Counter: {count()}</h1>

49

<button onClick={() => setCount(c => c + 1)}>

50

Increment

51

</button>

52

</div>

53

);

54

}

55

56

// Render to DOM

57

const dispose = render(() => <App />, document.getElementById("app")!);

58

59

// Later cleanup

60

// dispose();

61

62

// Server-side rendering hydration

63

function HydratedApp() {

64

const [data, setData] = createSignal("Initial data");

65

66

return (

67

<div>

68

<h1>Hydrated App</h1>

69

<p>{data()}</p>

70

<button onClick={() => setData("Updated data")}>

71

Update

72

</button>

73

</div>

74

);

75

}

76

77

// Hydrate server-rendered content

78

const disposeHydration = hydrate(

79

() => <HydratedApp />,

80

document.getElementById("hydration-root")!

81

);

82

```

83

84

### Web Components

85

86

Create web components and portals for rendering content outside the normal component tree.

87

88

```typescript { .api }

89

/**

90

* Renders components somewhere else in the DOM

91

* @param props - Portal component props

92

* @returns JSX element that renders children to different location

93

*/

94

function Portal<T>(props: {

95

mount?: Node;

96

useShadow?: boolean;

97

isSVG?: boolean;

98

ref?: T;

99

children: JSX.Element;

100

}): JSX.Element;

101

102

/**

103

* Renders an arbitrary custom or native component and passes the other props

104

* @param props - Dynamic component props

105

* @returns JSX element based on component prop

106

*/

107

function Dynamic<T extends ValidComponent>(props: {

108

component: T;

109

[key: string]: any;

110

}): JSX.Element;

111

112

/**

113

* Lower level version of Dynamic component for performance optimization

114

* @param component - Component to render dynamically

115

* @param props - Props to pass to component

116

* @returns JSX element

117

*/

118

function createDynamic<T>(

119

component: () => T | string | undefined,

120

props: () => any

121

): JSX.Element;

122

123

type ValidComponent = string | Component<any> | (keyof JSX.IntrinsicElements);

124

```

125

126

**Usage Examples:**

127

128

```typescript

129

import { Portal, Dynamic, createSignal } from "solid-js/web";

130

131

// Portal example - render modal outside main tree

132

function Modal(props: { isOpen: boolean; children: JSX.Element }) {

133

return (

134

<Show when={props.isOpen}>

135

<Portal mount={document.body}>

136

<div class="modal-backdrop">

137

<div class="modal">

138

{props.children}

139

</div>

140

</div>

141

</Portal>

142

</Show>

143

);

144

}

145

146

// Portal with shadow DOM

147

function IsolatedWidget(props: { children: JSX.Element }) {

148

return (

149

<Portal useShadow={true}>

150

<div class="isolated-widget">

151

{props.children}

152

</div>

153

</Portal>

154

);

155

}

156

157

// Dynamic component rendering

158

function DynamicExample() {

159

const [componentType, setComponentType] = createSignal<"button" | "input" | "div">("button");

160

const [customComponent, setCustomComponent] = createSignal<Component<any> | null>(null);

161

162

const handleLoad = async () => {

163

const module = await import("./CustomComponent");

164

setCustomComponent(() => module.default);

165

};

166

167

return (

168

<div>

169

<h2>Dynamic Component Example</h2>

170

171

{/* Dynamic native elements */}

172

<Dynamic

173

component={componentType()}

174

onClick={() => console.log("Clicked")}

175

value={componentType() === "input" ? "Input value" : undefined}

176

>

177

{componentType() === "button" ? "Click me" : "Dynamic content"}

178

</Dynamic>

179

180

<div>

181

<button onClick={() => setComponentType("button")}>Button</button>

182

<button onClick={() => setComponentType("input")}>Input</button>

183

<button onClick={() => setComponentType("div")}>Div</button>

184

</div>

185

186

{/* Dynamic custom components */}

187

<button onClick={handleLoad}>Load Custom Component</button>

188

189

<Show when={customComponent()}>

190

{(Component) => (

191

<Dynamic

192

component={Component}

193

title="Dynamic Title"

194

data={{ message: "Hello from dynamic component" }}

195

/>

196

)}

197

</Show>

198

</div>

199

);

200

}

201

202

// Using createDynamic for performance-critical scenarios

203

function OptimizedDynamic() {

204

const [tag, setTag] = createSignal("div");

205

const [props, setProps] = createSignal({ class: "dynamic" });

206

207

const element = createDynamic(

208

() => tag(),

209

() => ({

210

...props(),

211

children: `Dynamic ${tag()} element`

212

})

213

);

214

215

return (

216

<div>

217

{element}

218

<button onClick={() => setTag(tag() === "div" ? "span" : "div")}>

219

Toggle Tag

220

</button>

221

</div>

222

);

223

}

224

```

225

226

### DOM Manipulation Utilities

227

228

Low-level utilities for direct DOM manipulation and event handling.

229

230

```typescript { .api }

231

/**

232

* Insert content into DOM element

233

* @param parent - Parent element

234

* @param accessor - Content to insert

235

* @param marker - Optional marker for insertion point

236

* @param initial - Initial content

237

*/

238

function insert(

239

parent: Element,

240

accessor: (() => any) | any,

241

marker?: Node | null,

242

initial?: any

243

): any;

244

245

/**

246

* Spread props onto DOM element

247

* @param node - Target DOM element

248

* @param props - Props to spread

249

* @param isSVG - Whether element is SVG

250

* @param skipChildren - Whether to skip children prop

251

*/

252

function spread<T extends Element>(

253

node: T,

254

props: any,

255

isSVG?: boolean,

256

skipChildren?: boolean

257

): void;

258

259

/**

260

* Set attribute on DOM element

261

* @param node - Target DOM element

262

* @param name - Attribute name

263

* @param value - Attribute value

264

*/

265

function setAttribute(node: Element, name: string, value: any): void;

266

267

/**

268

* Manage element class list

269

* @param node - Target DOM element

270

* @param value - Object with class names as keys and boolean values

271

*/

272

function classList(

273

node: Element,

274

value: { [k: string]: boolean } | string

275

): void;

276

277

/**

278

* Set element styles

279

* @param node - Target DOM element

280

* @param value - Style object or string

281

*/

282

function style(

283

node: Element,

284

value: { [k: string]: string | number | undefined } | string

285

): void;

286

```

287

288

**Usage Examples:**

289

290

```typescript

291

import {

292

insert,

293

spread,

294

setAttribute,

295

classList,

296

style,

297

createSignal,

298

createEffect

299

} from "solid-js/web";

300

301

function DOMUtilitiesExample() {

302

let divRef: HTMLDivElement;

303

const [isActive, setIsActive] = createSignal(false);

304

const [content, setContent] = createSignal("Initial content");

305

306

createEffect(() => {

307

// Direct DOM manipulation using utilities

308

if (divRef) {

309

// Set attributes

310

setAttribute(divRef, "data-state", isActive() ? "active" : "inactive");

311

312

// Manage classes

313

classList(divRef, {

314

active: isActive(),

315

inactive: !isActive(),

316

"has-content": content().length > 0

317

});

318

319

// Set styles

320

style(divRef, {

321

"background-color": isActive() ? "#007bff" : "#6c757d",

322

"border-radius": "4px",

323

padding: "10px",

324

transition: "all 0.3s ease"

325

});

326

327

// Insert content

328

insert(divRef, content);

329

}

330

});

331

332

return (

333

<div>

334

<div ref={divRef!} />

335

336

<div class="controls">

337

<button onClick={() => setIsActive(!isActive())}>

338

Toggle Active

339

</button>

340

<input

341

type="text"

342

value={content()}

343

onInput={(e) => setContent(e.target.value)}

344

placeholder="Enter content"

345

/>

346

</div>

347

</div>

348

);

349

}

350

351

// Using spread for dynamic props

352

function SpreadExample() {

353

const [buttonProps, setButtonProps] = createSignal({

354

class: "btn btn-primary",

355

disabled: false,

356

"data-testid": "dynamic-button"

357

});

358

359

let buttonRef: HTMLButtonElement;

360

361

createEffect(() => {

362

if (buttonRef) {

363

spread(buttonRef, buttonProps(), false);

364

}

365

});

366

367

const toggleDisabled = () => {

368

setButtonProps(prev => ({

369

...prev,

370

disabled: !prev.disabled,

371

class: prev.disabled ? "btn btn-primary" : "btn btn-secondary"

372

}));

373

};

374

375

return (

376

<div>

377

<button ref={buttonRef!} onClick={toggleDisabled}>

378

Dynamic Button

379

</button>

380

</div>

381

);

382

}

383

```

384

385

### Event Handling

386

387

Advanced event handling with delegation and custom event systems.

388

389

```typescript { .api }

390

/**

391

* Set up event delegation for specified events

392

* @param eventNames - Array of event names to delegate

393

*/

394

function delegateEvents(eventNames: string[]): void;

395

396

/**

397

* Use directive system for extending DOM elements

398

* @param fn - Directive function

399

* @param element - Target element

400

* @param accessor - Accessor for directive parameters

401

*/

402

function use<T>(

403

fn: (element: Element, accessor: () => T) => void,

404

element: Element,

405

accessor: () => T

406

): void;

407

```

408

409

**Usage Examples:**

410

411

```typescript

412

import { delegateEvents, use, createSignal } from "solid-js/web";

413

414

// Set up event delegation for better performance

415

delegateEvents(["click", "input", "change"]);

416

417

// Custom directive for focus management

418

function focus(element: Element, accessor: () => boolean) {

419

const shouldFocus = accessor();

420

createEffect(() => {

421

if (shouldFocus && element instanceof HTMLElement) {

422

element.focus();

423

}

424

});

425

}

426

427

// Custom directive for outside click detection

428

function clickOutside(

429

element: Element,

430

accessor: () => () => void

431

) {

432

const handler = accessor();

433

434

const handleClick = (e: Event) => {

435

if (!element.contains(e.target as Node)) {

436

handler();

437

}

438

};

439

440

document.addEventListener("click", handleClick);

441

442

onCleanup(() => {

443

document.removeEventListener("click", handleClick);

444

});

445

}

446

447

function DirectiveExample() {

448

const [isOpen, setIsOpen] = createSignal(false);

449

const [focusInput, setFocusInput] = createSignal(false);

450

451

return (

452

<div>

453

<button onClick={() => setIsOpen(!isOpen())}>

454

Toggle Dropdown

455

</button>

456

457

<Show when={isOpen()}>

458

<div

459

class="dropdown"

460

use:clickOutside={() => setIsOpen(false)}

461

>

462

<input

463

type="text"

464

placeholder="Search..."

465

use:focus={focusInput}

466

/>

467

<ul>

468

<li>Option 1</li>

469

<li>Option 2</li>

470

<li>Option 3</li>

471

</ul>

472

</div>

473

</Show>

474

475

<button onClick={() => setFocusInput(!focusInput())}>

476

Toggle Input Focus

477

</button>

478

</div>

479

);

480

}

481

482

// Declare custom directives for TypeScript

483

declare module "solid-js" {

484

namespace JSX {

485

interface Directives {

486

focus: boolean;

487

clickOutside: () => void;

488

}

489

}

490

}

491

```

492

493

### Server-Side Rendering Support

494

495

Constants and utilities for handling server vs client environments.

496

497

```typescript { .api }

498

/**

499

* Indicates if running on server (false in browser builds)

500

*/

501

const isServer: boolean;

502

503

/**

504

* Development mode flag

505

*/

506

const isDev: boolean;

507

```

508

509

**Usage Examples:**

510

511

```typescript

512

import { isServer, isDev } from "solid-js/web";

513

import { createSignal, onMount } from "solid-js";

514

515

function EnvironmentAwareComponent() {

516

const [clientData, setClientData] = createSignal<any>(null);

517

const [mounted, setMounted] = createSignal(false);

518

519

onMount(() => {

520

setMounted(true);

521

522

if (!isServer) {

523

// Client-only code

524

setClientData({

525

userAgent: navigator.userAgent,

526

viewport: {

527

width: window.innerWidth,

528

height: window.innerHeight

529

}

530

});

531

}

532

});

533

534

// Conditional rendering based on environment

535

const renderClientOnly = () => {

536

if (isServer) return null;

537

538

return (

539

<div class="client-only">

540

<h3>Client-only Content</h3>

541

<Show when={clientData()}>

542

{(data) => (

543

<div>

544

<p>User Agent: {data.userAgent}</p>

545

<p>Viewport: {data.viewport.width} x {data.viewport.height}</p>

546

</div>

547

)}

548

</Show>

549

</div>

550

);

551

};

552

553

return (

554

<div>

555

<h2>Environment Aware Component</h2>

556

557

<div>

558

<p>Is Server: {isServer ? "Yes" : "No"}</p>

559

<p>Is Dev: {isDev ? "Yes" : "No"}</p>

560

<p>Is Mounted: {mounted() ? "Yes" : "No"}</p>

561

</div>

562

563

{/* Always render on server, conditionally on client */}

564

<div class="universal">

565

<h3>Universal Content</h3>

566

<p>This renders on both server and client</p>

567

</div>

568

569

{/* Client-only content */}

570

{renderClientOnly()}

571

572

{/* Progressive enhancement */}

573

<Show when={mounted()}>

574

<div class="enhanced">

575

<h3>Enhanced Content</h3>

576

<p>This only appears after hydration</p>

577

</div>

578

</Show>

579

</div>

580

);

581

}

582

```

583

584

## Types

585

586

### Rendering Types

587

588

```typescript { .api }

589

type MountableElement = Element | Document | ShadowRoot | DocumentFragment | Node;

590

type ValidComponent = string | Component<any> | (keyof JSX.IntrinsicElements);

591

592

interface PortalProps {

593

mount?: Node;

594

useShadow?: boolean;

595

isSVG?: boolean;

596

ref?: any;

597

children: JSX.Element;

598

}

599

600

interface DynamicProps<T extends ValidComponent> {

601

component: T;

602

[key: string]: any;

603

}

604

```

605

606

### DOM Utility Types

607

608

```typescript { .api }

609

type ClassList = { [k: string]: boolean } | string;

610

type StyleObject = { [k: string]: string | number | undefined } | string;

611

612

interface DirectiveFunction<T> {

613

(element: Element, accessor: () => T): void;

614

}

615

```