solidjs
This guide provides definitive, opinionated best practices for writing high-performance, maintainable SolidJS applications, focusing on reactivity, component architecture, and common pitfalls.
La skill
# solidjs Best Practices
This document outlines the definitive best practices for developing with SolidJS 1.x. Adhere to these guidelines to leverage Solid's fine-grained reactivity, ensure optimal performance, and maintain a consistent, scalable codebase.
---
## 1. Use SolidJS 1.x (Stable)
**Always use the current stable 1.x release.** SolidJS 2.0 is experimental and not ready for production. Avoid any features or APIs mentioned for 2.0.
## 2. Component Architecture & Code Organization
### 2.1. Component File Naming
**Use PascalCase for component filenames.** This clearly distinguishes components from other modules.
* ❌ BAD: `mybutton.jsx`, `user-profile.jsx`
* ✅ GOOD: `MyButton.jsx`, `UserProfile.jsx`
### 2.2. Keep Components Lean
**Components should be small and focused.** Extract complex logic or UI parts into separate components or custom primitives.
### 2.3. Colocate Styles
**Use Vite's CSS Modules for component-specific styling.** This provides scoped styles without runtime overhead.
* ❌ BAD: Global CSS files, CSS-in-JS libraries with runtime.
* ✅ GOOD:
```jsx
// MyComponent.module.css
.container {
background-color: var(--color-primary);
}
.text {
color: white;
}
// MyComponent.jsx
import styles from './MyComponent.module.css';
function MyComponent() {
return <div class={styles.container}><span class={styles.text}>Hello</span></div>;
}
```
## 3. Props Handling
SolidJS props are reactive getters. Incorrect handling breaks reactivity.
### 3.1. Never Destructure Props Directly
**Do not destructure `props` at the component's top level.** This immediately loses reactivity. Access props via `props.propertyName`.
* ❌ BAD:
```jsx
function MyComponent({ name, age }) { // React pattern, breaks Solid reactivity
return <div>{name} is {age}</div>;
}
```
* ✅ GOOD:
```jsx
function MyComponent(props) {
return <div>{props.name} is {props.age}</div>; // Access directly
}
```
### 3.2. Apply Default Props with `mergeProps`
**Use `mergeProps` for default props.** This preserves reactivity and merges objects non-destructively.
* ❌ BAD:
```jsx
function MyComponent(props) {
const name = props.name || "Guest"; // Loses reactivity if props.name is a signal
return <div>Hello, {name}</div>;
}
```
* ✅ GOOD:
```jsx
import { mergeProps } from 'solid-js';
function MyComponent(props) {
const merged = mergeProps({ name: "Guest", greeting: "Hello" }, props);
return <div>{merged.greeting}, {merged.name}!</div>;
}
```
### 3.3. Use `splitProps` for Spreading
**When spreading props, use `splitProps` to separate known props from rest props.** This is crucial for accessibility and avoiding prop conflicts.
* ❌ BAD:
```jsx
function MyInput(props) {
return <input type="text" {...props} />; // Unsafe, can overwrite internal props
}
```
* ✅ GOOD:
```jsx
import { splitProps } from 'solid-js';
function MyInput(props) {
const [local, rest] = splitProps(props, ["label", "onChange"]);
return (
<label>
{local.label}
<input type="text" onChange={local.onChange} {...rest} />
</label>
);
}
```
## 4. State Management
### 4.1. Signals for Local State
**Use `createSignal` for simple, primitive, or local component state.** It's the most performant and idiomatic way to manage reactive values.
* ❌ BAD: `let count = 0;` (non-reactive)
* ✅ GOOD:
```jsx
import { createSignal } from 'solid-js';
function Counter() {
const [count, setCount] = createSignal(0);
return <button onClick={() => setCount(c => c + 1)}>{count()}</button>;
}
```
### 4.2. Stores for Complex/Global State
**Use `createStore` for nested, mutable objects or global state.** Stores provide deep reactivity for objects.
* ❌ BAD: `createSignal({ user: { name: "John" } })` (nested updates are not reactive without manual spreading)
* ✅ GOOD:
```jsx
import { createStore } from 'solid-js/store';
const [user, setUser] = createStore({ firstName: "John", lastName: "Doe" });
function UserProfile() {
return (
<div>
<p>Name: {user.firstName} {user.lastName}</p>
<button onClick={() => setUser("firstName", "Jane")}>Change Name</button>
</div>
);
}
```
## 5. Side Effects & Lifecycle
### 5.1. `createEffect` for Side Effects
**Encapsulate all side effects in `createEffect`.** Solid automatically tracks dependencies, ensuring effects run only when necessary.
* ❌ BAD: `console.log(mySignal());` outside `createEffect` (runs once)
* ✅ GOOD:
```jsx
import { createSignal, createEffect } from 'solid-js';
function Logger() {
const [value, setValue] = createSignal("initial");
createEffect(() => {
console.log("Value changed:", value()); // Runs on initial render and whenever value() changes
});
return <input onInput={(e) => setValue(e.target.value)} value={value()} />;
}
```
### 5.2. `onCleanup` for Resource Management
**Use `onCleanup` to dispose of resources (subscriptions, timers, event listeners).** It runs when the reactive scope (component, effect) is destroyed.
* ❌ BAD: Global `clearInterval` or `removeEventListener` without `onCleanup`.
* ✅ GOOD:
```jsx
import { createEffect, onCleanup } from 'solid-js';
function Timer() {
createEffect(() => {
const interval = setInterval(() => console.log("tick"), 1000);
onCleanup(() => clearInterval(interval)); // Cleans up when component/effect unmounts
});
return <div>Timer running...</div>;
}
```
## 6. Performance Considerations
### 6.1. Avoid Unnecessary Re-renders
Solid's fine-grained reactivity minimizes re-renders, but be mindful of creating new functions or objects inside JSX that aren't memoized.
* ❌ BAD:
```jsx
<MyComponent data={{ id: 1 }} /> // Creates new object on every parent update
```
* ✅ GOOD:
```jsx
const data = { id: 1 };
<MyComponent data={data} /> // Object reference remains stable
```
### 6.2. Lazy Loading
**Use dynamic imports for route-level components.** This reduces initial bundle size.
* ✅ GOOD:
```jsx
import { lazy } from 'solid-js';
import { Routes, Route } from '@solidjs/router';
const HomePage = lazy(() => import('./pages/Home'));
const AboutPage = lazy(() => import('./pages/About'));
function AppRoutes() {
return (
<Routes>
<Route path="/" component={HomePage} />
<Route path="/about" component={AboutPage} />
</Routes>
);
}
```
## 7. Ecosystem Tools
**Leverage official and community-vetted tools.**
* **Bundling**: `vite-plugin-solid`
* **Routing**: `solid-router` (or SolidStart)
* **Head Management**: `solid-meta`
* **Linting**: `eslint-plugin-solid`
* **Testing**: `solid-testing-library`
## 8. Accessibility
**Prioritize semantic HTML and WAI-ARIA attributes.** SolidJS does not abstract away the DOM, making direct accessibility implementation straightforward.
* ✅ GOOD:
```jsx
<button aria-label="Close dialog" onClick={closeDialog}>X</button>
<input type="checkbox" id="agree" checked={agreed()} onChange={toggleAgreed} />
<label for="agree">I agree to terms</label>
```
## 9. Testing Approaches
**Use `solid-testing-library` for unit and integration tests.** It provides a user-centric API similar to React Testing Library.
* ✅ GOOD:
```jsx
import { render, screen } from 'solid-testing-library';
import { createSignal } from 'solid-js';
import MyComponent from './MyComponent';
test('MyComponent displays the correct count', async () => {
const [count, setCount] = createSignal(0);
render(() => <MyComponent count={count()} />);
expect(screen.getByText(/Count: 0/i)).toBeInTheDocument();
setCount(1);
await Promise.resolve(); // Wait for Solid's microtask queue
expect(screen.getByText(/Count: 1/i)).toBeInTheDocument();
});
```
Cómo instalarla
1. Open the tool and start a new conversation, project or template. 2. Paste the text below wherever the tool keeps standing instructions — usually called system prompt, instructions, or persona. 3. Save it so it applies to every run rather than a single message.