A declarative JavaScript library for building user interfaces with fine-grained reactivity.
—
Built-in control flow components for conditional rendering, list rendering, and error boundaries with optimized updates and proper cleanup.
Render content conditionally based on boolean or truthy values with automatic cleanup.
/**
* Conditionally render its children or an optional fallback component
* @param props - Show component props
* @returns JSX element based on condition
*/
function Show<T>(props: {
when: T | undefined | null | false;
keyed?: boolean;
fallback?: JSX.Element;
children: JSX.Element | ((item: NonNullable<T> | Accessor<NonNullable<T>>) => JSX.Element);
}): JSX.Element;Usage Examples:
import { Show, createSignal } from "solid-js";
function ConditionalContent() {
const [user, setUser] = createSignal<{ name: string; email: string } | null>(null);
const [loading, setLoading] = createSignal(false);
// Basic conditional rendering
return (
<div>
<Show when={!loading()} fallback={<div>Loading...</div>}>
<div>Content loaded!</div>
</Show>
{/* Conditional rendering with data access */}
<Show when={user()} fallback={<div>No user logged in</div>}>
{(userData) => (
<div>
<h1>Welcome, {userData.name}!</h1>
<p>Email: {userData.email}</p>
</div>
)}
</Show>
{/* Nested conditional rendering */}
<Show when={user()}>
<Show when={user()?.email} fallback={<p>No email provided</p>}>
<p>Contact: {user()!.email}</p>
</Show>
</Show>
</div>
);
}Handle mutually exclusive conditions with Switch and Match components.
/**
* Switches between content based on mutually exclusive conditions
* @param props - Switch component props
* @returns JSX element based on first matching condition
*/
function Switch(props: {
fallback?: JSX.Element;
children: JSX.Element;
}): JSX.Element;
/**
* Selects content based on condition when inside a Switch control flow
* @param props - Match component props
* @returns JSX element if condition matches
*/
function Match<T>(props: {
when: T | undefined | null | false;
keyed?: boolean;
children: JSX.Element | ((item: NonNullable<T> | Accessor<NonNullable<T>>) => JSX.Element);
}): JSX.Element;Usage Examples:
import { Switch, Match, createSignal } from "solid-js";
function StatusDisplay() {
const [status, setStatus] = createSignal<"loading" | "success" | "error" | "idle">("idle");
const [data, setData] = createSignal<any>(null);
const [error, setError] = createSignal<string | null>(null);
return (
<div>
<Switch fallback={<div>Unknown status</div>}>
<Match when={status() === "loading"}>
<div class="spinner">Loading...</div>
</Match>
<Match when={status() === "success" && data()}>
{(successData) => (
<div class="success">
<h2>Success!</h2>
<pre>{JSON.stringify(successData, null, 2)}</pre>
</div>
)}
</Match>
<Match when={status() === "error" && error()}>
{(errorMessage) => (
<div class="error">
<h2>Error occurred</h2>
<p>{errorMessage}</p>
<button onClick={() => setStatus("idle")}>Retry</button>
</div>
)}
</Match>
<Match when={status() === "idle"}>
<div>
<p>Ready to start</p>
<button onClick={() => setStatus("loading")}>Load Data</button>
</div>
</Match>
</Switch>
</div>
);
}Render lists of items with efficient keying and reactivity.
/**
* Creates a list of elements from a list with item-based keying
* @param props - For component props
* @returns Array of JSX elements
*/
function For<T extends readonly any[], U extends JSX.Element>(props: {
each: T | undefined | null | false;
children: (item: T[number], index: Accessor<number>) => U;
fallback?: JSX.Element;
}): JSX.Element;
/**
* Non-keyed iteration over a list creating elements from its items
* @param props - Index component props
* @returns Array of JSX elements
*/
function Index<T extends readonly any[], U extends JSX.Element>(props: {
each: T | undefined | null | false;
children: (item: Accessor<T[number]>, index: number) => U;
fallback?: JSX.Element;
}): JSX.Element;Usage Examples:
import { For, Index, createSignal } from "solid-js";
function ListExamples() {
const [users, setUsers] = createSignal([
{ id: 1, name: "John", age: 30 },
{ id: 2, name: "Jane", age: 25 },
{ id: 3, name: "Bob", age: 35 }
]);
const [numbers, setNumbers] = createSignal([1, 2, 3, 4, 5]);
return (
<div>
{/* For component - item-based keying (recommended for object arrays) */}
<h2>Users (For)</h2>
<ul>
<For each={users()} fallback={<li>No users found</li>}>
{(user, index) => (
<li>
<strong>{index() + 1}.</strong> {user.name} (Age: {user.age})
<button onClick={() => {
setUsers(prev => prev.filter(u => u.id !== user.id));
}}>
Remove
</button>
</li>
)}
</For>
</ul>
{/* Index component - index-based keying (better for simple arrays) */}
<h2>Numbers (Index)</h2>
<ul>
<Index each={numbers()} fallback={<li>No numbers</li>}>
{(number, index) => (
<li>
Position {index}: {number()}
<button onClick={() => {
setNumbers(prev => prev.filter((_, i) => i !== index));
}}>
Remove
</button>
</li>
)}
</Index>
</ul>
{/* Dynamic list updates */}
<div>
<button onClick={() => {
setUsers(prev => [...prev, {
id: Date.now(),
name: `User ${prev.length + 1}`,
age: Math.floor(Math.random() * 50) + 20
}]);
}}>
Add User
</button>
<button onClick={() => {
setNumbers(prev => [...prev, prev.length + 1]);
}}>
Add Number
</button>
</div>
</div>
);
}Catch and handle errors in component trees with recovery mechanisms.
/**
* Catches uncaught errors inside components and renders a fallback content
* @param props - ErrorBoundary component props
* @returns JSX element with error handling
*/
function ErrorBoundary(props: {
fallback: JSX.Element | ((err: any, reset: () => void) => JSX.Element);
children: JSX.Element;
}): JSX.Element;
/**
* Resets all error boundaries
*/
function resetErrorBoundaries(): void;Usage Examples:
import { ErrorBoundary, resetErrorBoundaries, createSignal } from "solid-js";
// Error boundary with component fallback
function ErrorFallback(props: { err: Error; reset: () => void }) {
return (
<div class="error-boundary">
<h2>Something went wrong</h2>
<details>
<summary>Error details</summary>
<pre>{props.err.message}</pre>
<pre>{props.err.stack}</pre>
</details>
<div>
<button onClick={props.reset}>Try again</button>
<button onClick={() => resetErrorBoundaries()}>Reset all</button>
</div>
</div>
);
}
// Component that might throw an error
function RiskyComponent() {
const [shouldError, setShouldError] = createSignal(false);
if (shouldError()) {
throw new Error("Something went wrong in RiskyComponent!");
}
return (
<div>
<p>This component works fine</p>
<button onClick={() => setShouldError(true)}>
Trigger Error
</button>
</div>
);
}
// App with error boundary
function App() {
return (
<div>
<h1>My App</h1>
<ErrorBoundary fallback={ErrorFallback}>
<RiskyComponent />
<div>Other content that should still render</div>
</ErrorBoundary>
{/* Error boundary with inline fallback */}
<ErrorBoundary
fallback={(err, reset) => (
<div class="inline-error">
<p>Error: {err.message}</p>
<button onClick={reset}>Retry</button>
</div>
)}
>
<AnotherRiskyComponent />
</ErrorBoundary>
</div>
);
}Control the order and rendering behavior of suspended content.
/**
* Controls the order in which suspended content is revealed (experimental)
* @param props - SuspenseList component props
* @returns JSX element with controlled suspense ordering
*/
function SuspenseList(props: {
children: JSX.Element;
revealOrder: "forwards" | "backwards" | "together";
tail?: "collapsed" | "hidden";
}): JSX.Element;Usage Examples:
import { SuspenseList, Suspense, For, createResource } from "solid-js";
function SequentialSuspenseExample() {
const [posts] = createResource(() => fetchPosts());
return (
<SuspenseList revealOrder="forwards" tail="collapsed">
<For each={posts()}>
{(post) => (
<Suspense fallback={<div>Loading post...</div>}>
<PostCard postId={post.id} />
</Suspense>
)}
</For>
</SuspenseList>
);
}
// Posts will be revealed in order, waiting for previous ones to complete
function PostCard(props: { postId: number }) {
const [postDetails] = createResource(() => fetchPostDetails(props.postId));
return (
<div class="post-card">
<h3>{postDetails()?.title}</h3>
<p>{postDetails()?.content}</p>
</div>
);
}Control flow components work seamlessly with Suspense for async operations.
Usage Examples:
import { For, Show, ErrorBoundary, Suspense, createResource } from "solid-js";
function AsyncListExample() {
const [users] = createResource(() => fetchUsers());
return (
<div>
<ErrorBoundary fallback={(err, reset) => (
<div>
<p>Failed to load users: {err.message}</p>
<button onClick={reset}>Retry</button>
</div>
)}>
<Suspense fallback={<div>Loading users...</div>}>
<Show when={users()} fallback={<div>No users available</div>}>
<For each={users()}>
{(user) => (
<div class="user-card">
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
)}
</For>
</Show>
</Suspense>
</ErrorBoundary>
</div>
);
}
async function fetchUsers() {
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error('Failed to fetch users');
}
return response.json();
}Control flow components in SolidJS are optimized for performance:
// Example of performance optimization
function OptimizedList() {
const [items, setItems] = createSignal([
{ id: 1, name: "Item 1", visible: true },
{ id: 2, name: "Item 2", visible: false },
{ id: 3, name: "Item 3", visible: true }
]);
// Only visible items are rendered
const visibleItems = createMemo(() =>
items().filter(item => item.visible)
);
return (
<For each={visibleItems()}>
{(item) => (
<div class="item">
{item.name}
<button
onClick={() => {
setItems(prev => prev.map(i =>
i.id === item.id
? { ...i, visible: !i.visible }
: i
));
}}
>
Toggle
</button>
</div>
)}
</For>
);
}Install with Tessl CLI
npx tessl i tessl/npm-solid-js