CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-solid-js

A declarative JavaScript library for building user interfaces with fine-grained reactivity.

Pending
Overview
Eval results
Files

control-flow.mddocs/

Control Flow

Built-in control flow components for conditional rendering, list rendering, and error boundaries with optimized updates and proper cleanup.

Capabilities

Conditional Rendering

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>
  );
}

Multiple Conditions

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>
  );
}

List Rendering

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>
  );
}

Error Boundaries

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>
  );
}

Advanced Suspense Control

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>
  );
}

Suspense Integration

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();
}

Performance Considerations

Control flow components in SolidJS are optimized for performance:

  • Fine-grained updates: Only the specific items that change are updated
  • Keyed reconciliation: For components use item identity for efficient updates
  • Lazy evaluation: Content is only created when conditions are met
  • Automatic cleanup: Resources are properly disposed when conditions change
// 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

docs

component-system.md

context-scoping.md

control-flow.md

index.md

reactive-primitives.md

resources-async.md

store-management.md

web-rendering.md

tile.json