SolidJS

Essential SolidJS syntax, reactivity primitives, and patterns for building high-performance web applications.

frameworks
solidjsjavascriptreactivefrontendtypescript

Basic Component

// Function components return JSX
function HelloWorld() {
  return <h1>Hello, World!</h1>;
}

// With props
function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}

Signals (Reactive State)

import { createSignal } from "solid-js";

function Counter() {
  // createSignal returns [getter, setter]
  const [count, setCount] = createSignal(0);

  return (
    <button onClick={() => setCount(count() + 1)}>
      Count: {count()}
    </button>
  );
}

Derived State

import { createSignal } from "solid-js";

function DoubleCounter() {
  const [count, setCount] = createSignal(0);
  
  // Derived values are just functions
  const doubled = () => count() * 2;

  return <p>Doubled: {doubled()}</p>;
}

Effects

import { createSignal, createEffect } from "solid-js";

function Logger() {
  const [count, setCount] = createSignal(0);

  // Runs when dependencies change
  createEffect(() => {
    console.log("Count changed:", count());
  });

  return <button onClick={() => setCount(c => c + 1)}>Increment</button>;
}

Memos (Cached Computations)

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

function ExpensiveCalculation() {
  const [count, setCount] = createSignal(0);

  // Only recalculates when count changes
  const expensive = createMemo(() => {
    return count() * 2;
  });

  return <p>Result: {expensive()}</p>;
}

Stores (Nested Reactivity)

import { createStore } from "solid-js/store";

function TodoApp() {
  const [state, setState] = createStore({
    todos: [{ id: 1, text: "Learn Solid", done: false }]
  });

  // Update nested properties
  const toggleTodo = (id) => {
    setState("todos", todo => todo.id === id, "done", done => !done);
  };

  // Add new item
  const addTodo = (text) => {
    setState("todos", todos => [...todos, { id: Date.now(), text, done: false }]);
  };
}

Conditional Rendering

import { Show } from "solid-js";

function ConditionalContent(props) {
  return (
    <Show when={props.isLoggedIn} fallback={<p>Please log in</p>}>
      <p>Welcome back!</p>
    </Show>
  );
}

List Rendering

import { For } from "solid-js";

function TodoList(props) {
  return (
    <ul>
      <For each={props.todos}>
        {(todo, index) => (
          <li>{index() + 1}: {todo.text}</li>
        )}
      </For>
    </ul>
  );
}

Switch/Match

import { Switch, Match } from "solid-js";

function StatusMessage(props) {
  return (
    <Switch fallback={<p>Unknown status</p>}>
      <Match when={props.status === "loading"}>
        <p>Loading...</p>
      </Match>
      <Match when={props.status === "success"}>
        <p>Success!</p>
      </Match>
      <Match when={props.status === "error"}>
        <p>Error occurred</p>
      </Match>
    </Switch>
  );
}

Dynamic Components

import { Dynamic } from "solid-js/web";

function DynamicComponent(props) {
  return <Dynamic component={props.tag} class="dynamic">{props.children}</Dynamic>;
}

// Usage: <DynamicComponent tag="h1">Title</DynamicComponent>

Portal

import { Portal } from "solid-js/web";

function Modal(props) {
  return (
    <Portal mount={document.body}>
      <div class="modal">{props.children}</div>
    </Portal>
  );
}

Error Boundary

import { ErrorBoundary } from "solid-js";

function App() {
  return (
    <ErrorBoundary fallback={(err) => <p>Error: {err.message}</p>}>
      <RiskyComponent />
    </ErrorBoundary>
  );
}

Resources (Async Data)

import { createResource } from "solid-js";

async function fetchUser(id) {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

function UserProfile(props) {
  const [user] = createResource(() => props.id, fetchUser);

  return (
    <Show when={!user.loading} fallback={<p>Loading...</p>}>
      <p>{user()?.name}</p>
    </Show>
  );
}

Context

import { createContext, useContext } from "solid-js";

const ThemeContext = createContext("light");

function ThemeProvider(props) {
  return (
    <ThemeContext.Provider value={props.theme}>
      {props.children}
    </ThemeContext.Provider>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button class={theme}>Click me</button>;
}

Refs

function InputWithFocus() {
  let inputRef;

  const focusInput = () => inputRef.focus();

  return (
    <>
      <input ref={inputRef} />
      <button onClick={focusInput}>Focus Input</button>
    </>
  );
}

Event Handling

function EventExamples() {
  // Basic event handler
  const handleClick = (e) => console.log("Clicked!", e);

  // With parameters using bind
  const handleItemClick = (id, e) => console.log("Item:", id);

  return (
    <>
      <button onClick={handleClick}>Click Me</button>
      <button onClick={[handleItemClick, 123]}>Item 123</button>
      <input onInput={(e) => console.log(e.target.value)} />
    </>
  );
}

Lifecycle with onMount and onCleanup

import { onMount, onCleanup } from "solid-js";

function Timer() {
  const [count, setCount] = createSignal(0);

  onMount(() => {
    // Runs once when component mounts
    const interval = setInterval(() => setCount(c => c + 1), 1000);
    
    onCleanup(() => {
      // Cleanup when component unmounts
      clearInterval(interval);
    });
  });

  return <p>Seconds: {count()}</p>;
}

Class and Style Bindings

function StyledComponent() {
  const [isActive, setActive] = createSignal(false);

  return (
    <>
      {/* Class binding */}
      <div class={isActive() ? "active" : ""}>Toggle class</div>
      <div classList={{ active: isActive(), highlight: true }}>Multiple classes</div>
      
      {/* Style binding */}
      <div style={{ color: isActive() ? "red" : "blue" }}>Styled text</div>
    </>
  );
}

Spread Props

function Button(props) {
  // Split local props from passed-through props
  const [local, others] = splitProps(props, ["children", "class"]);

  return (
    <button class={`btn ${local.class}`} {...others}>
      {local.children}
    </button>
  );
}

Batch Updates

import { batch, createSignal } from "solid-js";

function BatchExample() {
  const [firstName, setFirstName] = createSignal("");
  const [lastName, setLastName] = createSignal("");

  const updateBoth = () => {
    // Batch multiple updates to prevent unnecessary re-renders
    batch(() => {
      setFirstName("John");
      setLastName("Doe");
    });
  };
}