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
```