Skip to content
All essays
WebMarch 27, 202612 min

SolidJS Fundamentals: A Complete Guide to Modern Reactive UI Development

Master SolidJS fundamentals with this comprehensive guide covering components, JSX, reactivity, and core concepts for building high-performance applications.

Ü
Ümit Uz
Mobile & Full Stack Developer

SolidJS is a revolutionary JavaScript framework that combines the simplicity of React with the performance of fine-grained reactivity. Unlike traditional frameworks that rely on Virtual DOM, SolidJS compiles your templates to efficient DOM updates, resulting in exceptional performance without the complexity.

What Makes SolidJS Different?

SolidJS stands out in the crowded JavaScript framework landscape through its unique approach to reactivity and rendering. While React uses a Virtual DOM and requires manual optimization through memoization and callbacks, SolidJS employs a reactive system that automatically tracks dependencies and updates only what changes.

Key Features

  • No Virtual DOM: Direct DOM manipulation for maximum performance
  • True Reactivity: Fine-grained updates without manual optimization
  • Simple Mental Model: Easy to understand and reason about
  • Small Bundle Size: Only 7KB minified and gzipped
  • React-like Syntax: Familiar JSX and component patterns
  • Excellent TypeScript Support: First-class TypeScript integration

Installation and Setup

Getting started with SolidJS is straightforward. You can create a new project using the official CLI:

bash
npm create solid@latest my-solid-app
cd my-solid-app
npm install
npm run dev

Or add SolidJS to an existing project:

bash
npm install solid-js

Project Structure

A typical SolidJS project follows this structure:

my-solid-app/
├── src/
│   ├── components/
│   ├── pages/
│   ├── App.tsx
│   ├── index.tsx
│   └── index.css
├── public/
├── package.json
└── tsconfig.json

Components in SolidJS

SolidJS components are simple functions that return JSX. They're lightweight and don't require class components or complex lifecycle methods.

tsx
import { createSignal } from 'solid-js';

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

  return (
    <div>
      <h1>Counter: {count()}</h1>
      <button onClick={() => setCount(count() + 1)}>
        Increment
      </button>
    </div>
  );
}

export default Counter;

Component Best Practices

  1. 1Keep Components Small: Single responsibility principle
  2. 2Use Descriptive Names: Clear component naming
  3. 3Avoid Side Effects: Keep rendering pure
  4. 4Props Destructuring: Clear prop dependencies
tsx
interface UserCardProps {
  name: string;
  email: string;
  avatar?: string;
}

function UserCard(props: UserCardProps) {
  const { name, email, avatar } = props;

  return (
    <div class="user-card">
      <Show when={avatar}>
        <img src={avatar} alt={name} />
      </Show>
      <h2>{name}</h2>
      <p>{email}</p>
    </div>
  );
}

JSX Syntax in SolidJS

SolidJS uses JSX with some unique characteristics:

Event Handlers

Event handlers in SolidJS use camelCase naming:

tsx
function Form() {
  const handleSubmit = (e: Event) => {
    e.preventDefault();
    // Handle form submission
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" placeholder="Enter text" />
      <button type="submit">Submit</button>
    </form>
  );
}

Class vs ClassName

SolidJS uses class instead of className:

tsx
function Button() {
  return (
    <button class="btn btn-primary">
      Click Me
    </button>
  );
}

Conditional Rendering

Use Show for conditional rendering:

tsx
import { Show } from 'solid-js';

function UserGreeting() {
  const [isLoggedIn, setIsLoggedIn] = createSignal(false);

  return (
    <div>
      <Show when={isLoggedIn()} fallback={<button onClick={() => setIsLoggedIn(true)}>Login</button>}>
        <h1>Welcome back!</h1>
        <button onClick={() => setIsLoggedIn(false)}>Logout</button>
      </Show>
    </div>
  );
}

List Rendering

Use For for efficient list rendering:

tsx
import { For } from 'solid-js';

function TodoList() {
  const [todos, setTodos] = createSignal([
    { id: 1, text: 'Learn SolidJS', completed: false },
    { id: 2, text: 'Build an app', completed: false },
  ]);

  return (
    <ul>
      <For each={todos()}>
        {(todo) => (
          <li>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={(e) => {
                // Update logic here
              }}
            />
            <span>{todo.text}</span>
          </li>
        )}
      </For>
    </ul>
  );
}

Signals and State

Signals are the foundation of reactivity in SolidJS. A signal is a container for a value that can be observed and updated.

tsx
import { createSignal } from 'solid-js';

function App() {
  const [name, setName] = createSignal('SolidJS');
  const [age, setAge] = createSignal(25);

  return (
    <div>
      <p>Name: {name()}</p>
      <p>Age: {age()}</p>
      <button onClick={() => setName('SolidJS Framework')}>
        Update Name
      </button>
    </div>
  );
}

Computed Values

Use derived values with functions:

tsx
function UserProfile() {
  const [firstName, setFirstName] = createSignal('John');
  const [lastName, setLastName] = createSignal('Doe');

  const fullName = () => `${firstName()} ${lastName()}`;

  return (
    <div>
      <h1>{fullName()}</h1>
      <input
        value={firstName()}
        onInput={(e) => setFirstName(e.currentTarget.value)}
      />
    </div>
  );
}

Effects and Lifecycle

Effects let you run code in response to signal changes:

tsx
import { createEffect } from 'solid-js';

function DocumentTitle() {
  const [title, setTitle] = createSignal('Home');

  createEffect(() => {
    document.title = title();
  });

  return (
    <input
      value={title()}
      onInput={(e) => setTitle(e.currentTarget.value)}
    />
  );
}

Cleanup Effects

Return a cleanup function from your effect:

tsx
createEffect(() => {
  const timer = setInterval(() => {
    console.log('Tick');
  }, 1000);

  return () => clearInterval(timer);
});

Props and Component Communication

SolidJS makes component communication straightforward:

tsx
interface ParentProps {
  initialCount: number;
}

function Parent(props: ParentProps) {
  const [count, setCount] = createSignal(props.initialCount);

  return <Child count={count()} onIncrement={() => setCount(c => c + 1)} />;
}

interface ChildProps {
  count: number;
  onIncrement: () => void;
}

function Child(props: ChildProps) {
  return (
    <div>
      <span>Count: {props.count}</span>
      <button onClick={props.onIncrement}>+</button>
    </div>
  );
}

Refs and Direct DOM Access

Access DOM elements directly using refs:

tsx
function InputFocus() {
  let inputRef: HTMLInputElement;

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

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

Error Boundaries

Handle errors gracefully with Error Boundaries:

tsx
import { ErrorBoundary } from 'solid-js';

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

Performance Optimization

SolidJS is already highly optimized, but these tips help maximize performance:

  1. 1Avoid Inline Functions: Define handlers outside render
  2. 2Use For, Not map: For efficient list rendering
  3. 3Leverage Signals: Reactive values update efficiently
  4. 4Memoize Expensive Computations: Use createMemo
tsx
import { createMemo } from 'solid-js';

function ExpensiveComponent() {
  const [data, setData] = createSignal(largeDataSet);

  const processedData = createMemo(() => {
    // Expensive computation
    return data().map(item => /* complex processing */);
  });

  return <div>{/* Use processedData() */}</div>;
}

Testing SolidJS Applications

SolidJS provides excellent testing utilities:

tsx
import { render } from 'solid-js/web';
import { screen } from 'solid-testing-library';
import { expect, test } from 'vitest';

import Counter from './Counter';

test('increments count', () => {
  render(() => <Counter />);
  const button = screen.getByText('Increment');
  button.click();
  expect(screen.getByText('Count: 1')).toBeInTheDocument();
});

Common Patterns

Custom Hooks

tsx
function useCounter(initialValue = 0) {
  const [count, setCount] = createSignal(initialValue);
  const increment = () => setCount(c => c + 1);
  const decrement = () => setCount(c => c - 1);
  const reset = () => setCount(initialValue);

  return { count, increment, decrement, reset };
}

Context Pattern

tsx
import { createContext, useContext } from 'solid-js';

const ThemeContext = createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Child />
    </ThemeContext.Provider>
  );
}

function Child() {
  const theme = useContext(ThemeContext);
  return <div class={theme()}>Content</div>;
}

Best Practices

  1. 1Use TypeScript: Leverage type safety
  2. 2Keep Components Pure: Avoid side effects in render
  3. 3Use SolidJS Primitives: Leverage built-in functions
  4. 4Organize by Feature: Group related components
  5. 5Write Tests: Ensure reliability
  6. 6Follow Naming Conventions: Clear, descriptive names
  7. 7Optimize Bundle Size: Code splitting and lazy loading

Conclusion

SolidJS provides a powerful, performant, and developer-friendly framework for building modern web applications. Its unique approach to reactivity and rendering eliminates the need for Virtual DOM while maintaining a simple mental model. By mastering these fundamentals, you're well-equipped to build efficient, scalable applications with SolidJS.

The combination of React-like syntax, true reactivity, and exceptional performance makes SolidJS an excellent choice for projects that demand both developer experience and runtime performance. As you continue your SolidJS journey, explore advanced topics like stores, routing, and server-side rendering to unlock even more possibilities.

Enjoyed this essay? Share it.

Related essays

Next essay
SolidJS Stores: Advanced State Management for Complex Applications