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:
npm create solid@latest my-solid-app
cd my-solid-app
npm install
npm run devOr add SolidJS to an existing project:
npm install solid-jsProject Structure
A typical SolidJS project follows this structure:
my-solid-app/
├── src/
│ ├── components/
│ ├── pages/
│ ├── App.tsx
│ ├── index.tsx
│ └── index.css
├── public/
├── package.json
└── tsconfig.jsonComponents in SolidJS
SolidJS components are simple functions that return JSX. They're lightweight and don't require class components or complex lifecycle methods.
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
- 1Keep Components Small: Single responsibility principle
- 2Use Descriptive Names: Clear component naming
- 3Avoid Side Effects: Keep rendering pure
- 4Props Destructuring: Clear prop dependencies
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:
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:
function Button() {
return (
<button class="btn btn-primary">
Click Me
</button>
);
}Conditional Rendering
Use Show for conditional rendering:
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:
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.
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:
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:
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:
createEffect(() => {
const timer = setInterval(() => {
console.log('Tick');
}, 1000);
return () => clearInterval(timer);
});Props and Component Communication
SolidJS makes component communication straightforward:
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:
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:
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:
- 1Avoid Inline Functions: Define handlers outside render
- 2Use For, Not map: For efficient list rendering
- 3Leverage Signals: Reactive values update efficiently
- 4Memoize Expensive Computations: Use
createMemo
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:
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
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
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
- 1Use TypeScript: Leverage type safety
- 2Keep Components Pure: Avoid side effects in render
- 3Use SolidJS Primitives: Leverage built-in functions
- 4Organize by Feature: Group related components
- 5Write Tests: Ensure reliability
- 6Follow Naming Conventions: Clear, descriptive names
- 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.