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

component-system.mddocs/

Component System

Function-based component system with props utilities, lifecycle hooks, and component creation functions for building modular UI components.

Capabilities

Component Functions

Create and manage component instances with optimized rendering and lifecycle management.

/**
 * Creates a component instance
 * @param Comp - Component function to instantiate
 * @param props - Props to pass to the component
 * @returns JSX element
 */
function createComponent<T extends Component<any>>(
  Comp: T,
  props: ComponentProps<T>
): JSX.Element;

/**
 * Lazy load a function component asynchronously
 * @param fn - Function that returns a promise with the component
 * @returns Lazy component with preload capability
 */
function lazy<T extends Component<any>>(
  fn: () => Promise<{ default: T }>
): T & { preload: () => Promise<{ default: T }> };

/**
 * Creates a unique identifier for components
 * @returns Unique string identifier
 */
function createUniqueId(): string;

Usage Examples:

import { createComponent, lazy } from "solid-js";

// Creating component instances
function App() {
  return createComponent(UserProfile, { 
    name: "John", 
    age: 30 
  });
}

// Lazy loading components
const LazyUserDashboard = lazy(() => import("./UserDashboard"));

function App() {
  return (
    <div>
      <LazyUserDashboard userId="123" />
    </div>
  );
}

// Preloading lazy components
LazyUserDashboard.preload();

// Using unique IDs
function FormField() {
  const id = createUniqueId();
  return (
    <div>
      <label for={id}>Name:</label>
      <input id={id} type="text" />
    </div>
  );
}

Props Utilities

Merge and split props objects with reactive updates and type safety.

/**
 * Merges multiple props objects reactively
 * @param sources - Props objects to merge
 * @returns Merged props object
 */
function mergeProps<T extends Record<string, any>[]>(
  ...sources: T
): MergeProps<T>;

/**
 * Splits props into multiple objects based on keys
 * @param props - Props object to split
 * @param keys - Arrays of keys to extract into separate objects
 * @returns Array of split props objects
 */
function splitProps<T extends Record<string, any>, K extends (keyof T)[][]>(
  props: T,
  ...keys: K
): SplitProps<T, K>;

type MergeProps<T extends Record<string, any>[]> = T extends [
  infer First,
  ...infer Rest
]
  ? First & MergeProps<Rest>
  : {};

type SplitProps<T, K> = K extends [infer First, ...infer Rest]
  ? First extends (keyof T)[]
    ? [Pick<T, First[number]>, ...SplitProps<Omit<T, First[number]>, Rest>]
    : []
  : [T];

Usage Examples:

import { mergeProps, splitProps, createSignal } from "solid-js";

// Merging props
function Button(props) {
  const merged = mergeProps(
    { type: "button", disabled: false },
    props
  );
  
  return <button {...merged} />;
}

// Splitting props
function Input(props) {
  const [inputProps, otherProps] = splitProps(props, ["value", "onInput", "placeholder"]);
  
  return (
    <div {...otherProps}>
      <input {...inputProps} />
    </div>
  );
}

// Complex props handling
function CustomComponent(props) {
  const [local, buttonProps, divProps] = splitProps(
    props,
    ["label", "required"],
    ["onClick", "disabled"],
    ["class", "style"]
  );

  const defaultProps = { disabled: false, required: false };
  const mergedLocal = mergeProps(defaultProps, local);

  return (
    <div {...divProps}>
      {mergedLocal.required && <span>*</span>}
      <label>{mergedLocal.label}</label>
      <button {...buttonProps} disabled={mergedLocal.disabled || buttonProps.disabled}>
        Click me
      </button>
    </div>
  );
}

Children Utilities

Resolve and interact with component children in reactive contexts.

/**
 * Resolves child elements to help interact with children
 * @param fn - Accessor function that returns JSX children
 * @returns Resolved children with helper methods
 */
function children(fn: Accessor<JSX.Element>): ChildrenReturn;

interface ChildrenReturn {
  (): ResolvedJSXElement;
  toArray(): ResolvedJSXElement[];
}

type ResolvedJSXElement = Element | Text | string | number;

Usage Examples:

import { children, For } from "solid-js";

// Resolving children
function Container(props) {
  const c = children(() => props.children);
  
  return (
    <div class="container">
      <div class="header">Container has {c.toArray().length} children</div>
      {c()}
    </div>
  );
}

// Working with children in custom components
function List(props) {
  const c = children(() => props.children);
  
  return (
    <ul>
      <For each={c.toArray()}>
        {(child, index) => (
          <li data-index={index()}>
            {child}
          </li>
        )}
      </For>
    </ul>
  );
}

// Usage
function App() {
  return (
    <Container>
      <span>First child</span>
      <span>Second child</span>
      <span>Third child</span>
    </Container>
  );
}

Component Types

Basic Component Types

/**
 * A general Component has no implicit children prop
 */
type Component<P = {}> = (props: P) => JSX.Element;

/**
 * Component that forbids children prop
 */
type VoidComponent<P = {}> = Component<VoidProps<P>>;

/**
 * Component that allows optional children prop
 */
type ParentComponent<P = {}> = Component<ParentProps<P>>;

/**
 * Component that requires children prop with specific type
 */
type FlowComponent<P, C> = Component<FlowProps<P, C>>;

Props Types

type VoidProps<P> = P & { children?: never };
type ParentProps<P> = P & { children?: JSX.Element };
type FlowProps<P, C> = P & { children: C };

type ComponentProps<T extends keyof JSX.IntrinsicElements | Component<any>> = 
  T extends Component<infer P> 
    ? P 
    : T extends keyof JSX.IntrinsicElements 
      ? JSX.IntrinsicElements[T] 
      : {};

JSX Types

declare namespace JSX {
  type Element = any;
  
  interface IntrinsicElements {
    [elemName: string]: any;
  }
  
  interface ElementChildrenAttribute {
    children: {};
  }
}

Advanced Patterns

Higher-Order Components

import { Component, createMemo } from "solid-js";

// HOC for adding loading state
function withLoading<P extends object>(
  WrappedComponent: Component<P>
): Component<P & { loading?: boolean }> {
  return (props) => {
    const loading = createMemo(() => props.loading ?? false);
    
    if (loading()) {
      return <div>Loading...</div>;
    }
    
    return <WrappedComponent {...props} />;
  };
}

// Usage
const UserProfileWithLoading = withLoading(UserProfile);

Render Props Pattern

import { Component, JSX } from "solid-js";

interface RenderPropsComponent<T> {
  children: (data: T) => JSX.Element;
  data: T;
}

function DataProvider<T>(props: RenderPropsComponent<T>): JSX.Element {
  return <div>{props.children(props.data)}</div>;
}

// Usage
function App() {
  return (
    <DataProvider data={{ name: "John", age: 30 }}>
      {(user) => (
        <div>
          <h1>{user.name}</h1>
          <p>Age: {user.age}</p>
        </div>
      )}
    </DataProvider>
  );
}

Component Composition

import { ParentComponent, JSX } from "solid-js";

// Compound component pattern
const Card: ParentComponent & {
  Header: ParentComponent;
  Body: ParentComponent;
  Footer: ParentComponent;
} = (props) => {
  return <div class="card">{props.children}</div>;
};

Card.Header = (props) => <div class="card-header">{props.children}</div>;
Card.Body = (props) => <div class="card-body">{props.children}</div>;
Card.Footer = (props) => <div class="card-footer">{props.children}</div>;

// Usage
function App() {
  return (
    <Card>
      <Card.Header>
        <h2>Card Title</h2>
      </Card.Header>
      <Card.Body>
        <p>Card content goes here.</p>
      </Card.Body>
      <Card.Footer>
        <button>Action</button>
      </Card.Footer>
    </Card>
  );
}

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