A declarative JavaScript library for building user interfaces with fine-grained reactivity.
—
DOM rendering utilities, web components, and hydration functions for building web applications with server-side rendering support.
Render SolidJS applications to the DOM with efficient updates and lifecycle management.
/**
* Renders a reactive component tree to a DOM element
* @param code - Function that returns JSX to render
* @param element - DOM element to render into
* @returns Dispose function to cleanup the rendering
*/
function render(
code: () => JSX.Element,
element: MountableElement
): () => void;
/**
* Hydrates server-rendered content with client-side reactivity
* @param fn - Function that returns JSX to hydrate
* @param node - DOM element containing server-rendered content
* @returns Dispose function to cleanup hydration
*/
function hydrate(
fn: () => JSX.Element,
node: MountableElement
): () => void;
type MountableElement = Element | Document | ShadowRoot | DocumentFragment | Node;Usage Examples:
import { render, hydrate } from "solid-js/web";
import { createSignal } from "solid-js";
// Basic rendering
function App() {
const [count, setCount] = createSignal(0);
return (
<div>
<h1>Counter: {count()}</h1>
<button onClick={() => setCount(c => c + 1)}>
Increment
</button>
</div>
);
}
// Render to DOM
const dispose = render(() => <App />, document.getElementById("app")!);
// Later cleanup
// dispose();
// Server-side rendering hydration
function HydratedApp() {
const [data, setData] = createSignal("Initial data");
return (
<div>
<h1>Hydrated App</h1>
<p>{data()}</p>
<button onClick={() => setData("Updated data")}>
Update
</button>
</div>
);
}
// Hydrate server-rendered content
const disposeHydration = hydrate(
() => <HydratedApp />,
document.getElementById("hydration-root")!
);Create web components and portals for rendering content outside the normal component tree.
/**
* Renders components somewhere else in the DOM
* @param props - Portal component props
* @returns JSX element that renders children to different location
*/
function Portal<T>(props: {
mount?: Node;
useShadow?: boolean;
isSVG?: boolean;
ref?: T;
children: JSX.Element;
}): JSX.Element;
/**
* Renders an arbitrary custom or native component and passes the other props
* @param props - Dynamic component props
* @returns JSX element based on component prop
*/
function Dynamic<T extends ValidComponent>(props: {
component: T;
[key: string]: any;
}): JSX.Element;
/**
* Lower level version of Dynamic component for performance optimization
* @param component - Component to render dynamically
* @param props - Props to pass to component
* @returns JSX element
*/
function createDynamic<T>(
component: () => T | string | undefined,
props: () => any
): JSX.Element;
type ValidComponent = string | Component<any> | (keyof JSX.IntrinsicElements);Usage Examples:
import { Portal, Dynamic, createSignal } from "solid-js/web";
// Portal example - render modal outside main tree
function Modal(props: { isOpen: boolean; children: JSX.Element }) {
return (
<Show when={props.isOpen}>
<Portal mount={document.body}>
<div class="modal-backdrop">
<div class="modal">
{props.children}
</div>
</div>
</Portal>
</Show>
);
}
// Portal with shadow DOM
function IsolatedWidget(props: { children: JSX.Element }) {
return (
<Portal useShadow={true}>
<div class="isolated-widget">
{props.children}
</div>
</Portal>
);
}
// Dynamic component rendering
function DynamicExample() {
const [componentType, setComponentType] = createSignal<"button" | "input" | "div">("button");
const [customComponent, setCustomComponent] = createSignal<Component<any> | null>(null);
const handleLoad = async () => {
const module = await import("./CustomComponent");
setCustomComponent(() => module.default);
};
return (
<div>
<h2>Dynamic Component Example</h2>
{/* Dynamic native elements */}
<Dynamic
component={componentType()}
onClick={() => console.log("Clicked")}
value={componentType() === "input" ? "Input value" : undefined}
>
{componentType() === "button" ? "Click me" : "Dynamic content"}
</Dynamic>
<div>
<button onClick={() => setComponentType("button")}>Button</button>
<button onClick={() => setComponentType("input")}>Input</button>
<button onClick={() => setComponentType("div")}>Div</button>
</div>
{/* Dynamic custom components */}
<button onClick={handleLoad}>Load Custom Component</button>
<Show when={customComponent()}>
{(Component) => (
<Dynamic
component={Component}
title="Dynamic Title"
data={{ message: "Hello from dynamic component" }}
/>
)}
</Show>
</div>
);
}
// Using createDynamic for performance-critical scenarios
function OptimizedDynamic() {
const [tag, setTag] = createSignal("div");
const [props, setProps] = createSignal({ class: "dynamic" });
const element = createDynamic(
() => tag(),
() => ({
...props(),
children: `Dynamic ${tag()} element`
})
);
return (
<div>
{element}
<button onClick={() => setTag(tag() === "div" ? "span" : "div")}>
Toggle Tag
</button>
</div>
);
}Low-level utilities for direct DOM manipulation and event handling.
/**
* Insert content into DOM element
* @param parent - Parent element
* @param accessor - Content to insert
* @param marker - Optional marker for insertion point
* @param initial - Initial content
*/
function insert(
parent: Element,
accessor: (() => any) | any,
marker?: Node | null,
initial?: any
): any;
/**
* Spread props onto DOM element
* @param node - Target DOM element
* @param props - Props to spread
* @param isSVG - Whether element is SVG
* @param skipChildren - Whether to skip children prop
*/
function spread<T extends Element>(
node: T,
props: any,
isSVG?: boolean,
skipChildren?: boolean
): void;
/**
* Set attribute on DOM element
* @param node - Target DOM element
* @param name - Attribute name
* @param value - Attribute value
*/
function setAttribute(node: Element, name: string, value: any): void;
/**
* Manage element class list
* @param node - Target DOM element
* @param value - Object with class names as keys and boolean values
*/
function classList(
node: Element,
value: { [k: string]: boolean } | string
): void;
/**
* Set element styles
* @param node - Target DOM element
* @param value - Style object or string
*/
function style(
node: Element,
value: { [k: string]: string | number | undefined } | string
): void;Usage Examples:
import {
insert,
spread,
setAttribute,
classList,
style,
createSignal,
createEffect
} from "solid-js/web";
function DOMUtilitiesExample() {
let divRef: HTMLDivElement;
const [isActive, setIsActive] = createSignal(false);
const [content, setContent] = createSignal("Initial content");
createEffect(() => {
// Direct DOM manipulation using utilities
if (divRef) {
// Set attributes
setAttribute(divRef, "data-state", isActive() ? "active" : "inactive");
// Manage classes
classList(divRef, {
active: isActive(),
inactive: !isActive(),
"has-content": content().length > 0
});
// Set styles
style(divRef, {
"background-color": isActive() ? "#007bff" : "#6c757d",
"border-radius": "4px",
padding: "10px",
transition: "all 0.3s ease"
});
// Insert content
insert(divRef, content);
}
});
return (
<div>
<div ref={divRef!} />
<div class="controls">
<button onClick={() => setIsActive(!isActive())}>
Toggle Active
</button>
<input
type="text"
value={content()}
onInput={(e) => setContent(e.target.value)}
placeholder="Enter content"
/>
</div>
</div>
);
}
// Using spread for dynamic props
function SpreadExample() {
const [buttonProps, setButtonProps] = createSignal({
class: "btn btn-primary",
disabled: false,
"data-testid": "dynamic-button"
});
let buttonRef: HTMLButtonElement;
createEffect(() => {
if (buttonRef) {
spread(buttonRef, buttonProps(), false);
}
});
const toggleDisabled = () => {
setButtonProps(prev => ({
...prev,
disabled: !prev.disabled,
class: prev.disabled ? "btn btn-primary" : "btn btn-secondary"
}));
};
return (
<div>
<button ref={buttonRef!} onClick={toggleDisabled}>
Dynamic Button
</button>
</div>
);
}Advanced event handling with delegation and custom event systems.
/**
* Set up event delegation for specified events
* @param eventNames - Array of event names to delegate
*/
function delegateEvents(eventNames: string[]): void;
/**
* Use directive system for extending DOM elements
* @param fn - Directive function
* @param element - Target element
* @param accessor - Accessor for directive parameters
*/
function use<T>(
fn: (element: Element, accessor: () => T) => void,
element: Element,
accessor: () => T
): void;Usage Examples:
import { delegateEvents, use, createSignal } from "solid-js/web";
// Set up event delegation for better performance
delegateEvents(["click", "input", "change"]);
// Custom directive for focus management
function focus(element: Element, accessor: () => boolean) {
const shouldFocus = accessor();
createEffect(() => {
if (shouldFocus && element instanceof HTMLElement) {
element.focus();
}
});
}
// Custom directive for outside click detection
function clickOutside(
element: Element,
accessor: () => () => void
) {
const handler = accessor();
const handleClick = (e: Event) => {
if (!element.contains(e.target as Node)) {
handler();
}
};
document.addEventListener("click", handleClick);
onCleanup(() => {
document.removeEventListener("click", handleClick);
});
}
function DirectiveExample() {
const [isOpen, setIsOpen] = createSignal(false);
const [focusInput, setFocusInput] = createSignal(false);
return (
<div>
<button onClick={() => setIsOpen(!isOpen())}>
Toggle Dropdown
</button>
<Show when={isOpen()}>
<div
class="dropdown"
use:clickOutside={() => setIsOpen(false)}
>
<input
type="text"
placeholder="Search..."
use:focus={focusInput}
/>
<ul>
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
</ul>
</div>
</Show>
<button onClick={() => setFocusInput(!focusInput())}>
Toggle Input Focus
</button>
</div>
);
}
// Declare custom directives for TypeScript
declare module "solid-js" {
namespace JSX {
interface Directives {
focus: boolean;
clickOutside: () => void;
}
}
}Constants and utilities for handling server vs client environments.
/**
* Indicates if running on server (false in browser builds)
*/
const isServer: boolean;
/**
* Development mode flag
*/
const isDev: boolean;Usage Examples:
import { isServer, isDev } from "solid-js/web";
import { createSignal, onMount } from "solid-js";
function EnvironmentAwareComponent() {
const [clientData, setClientData] = createSignal<any>(null);
const [mounted, setMounted] = createSignal(false);
onMount(() => {
setMounted(true);
if (!isServer) {
// Client-only code
setClientData({
userAgent: navigator.userAgent,
viewport: {
width: window.innerWidth,
height: window.innerHeight
}
});
}
});
// Conditional rendering based on environment
const renderClientOnly = () => {
if (isServer) return null;
return (
<div class="client-only">
<h3>Client-only Content</h3>
<Show when={clientData()}>
{(data) => (
<div>
<p>User Agent: {data.userAgent}</p>
<p>Viewport: {data.viewport.width} x {data.viewport.height}</p>
</div>
)}
</Show>
</div>
);
};
return (
<div>
<h2>Environment Aware Component</h2>
<div>
<p>Is Server: {isServer ? "Yes" : "No"}</p>
<p>Is Dev: {isDev ? "Yes" : "No"}</p>
<p>Is Mounted: {mounted() ? "Yes" : "No"}</p>
</div>
{/* Always render on server, conditionally on client */}
<div class="universal">
<h3>Universal Content</h3>
<p>This renders on both server and client</p>
</div>
{/* Client-only content */}
{renderClientOnly()}
{/* Progressive enhancement */}
<Show when={mounted()}>
<div class="enhanced">
<h3>Enhanced Content</h3>
<p>This only appears after hydration</p>
</div>
</Show>
</div>
);
}type MountableElement = Element | Document | ShadowRoot | DocumentFragment | Node;
type ValidComponent = string | Component<any> | (keyof JSX.IntrinsicElements);
interface PortalProps {
mount?: Node;
useShadow?: boolean;
isSVG?: boolean;
ref?: any;
children: JSX.Element;
}
interface DynamicProps<T extends ValidComponent> {
component: T;
[key: string]: any;
}type ClassList = { [k: string]: boolean } | string;
type StyleObject = { [k: string]: string | number | undefined } | string;
interface DirectiveFunction<T> {
(element: Element, accessor: () => T): void;
}Install with Tessl CLI
npx tessl i tessl/npm-solid-js