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

web-rendering.mddocs/

Web Rendering

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

Capabilities

DOM Rendering

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")!
);

Web Components

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

DOM Manipulation Utilities

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

Event Handling

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

Server-Side Rendering Support

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

Types

Rendering Types

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

DOM Utility Types

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

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