Skip to main content
rule

solidjs — Cursor rule: what it does, how to install

A Cursor rule for SolidJS 1.x covering reactivity, component structure, state, effects, and cleanup in JavaScript and JSX files.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/solidjs.mdc
Open on GitHub

What solidjs does

This rule guides SolidJS 1.x work in files matching **/*.{js,jsx}. It keeps component props reactive by avoiding top-level destructuring, using mergeProps for defaults, and applying splitProps when known and rest props must be separated. Local primitive state uses createSignal; nested or shared objects use createStore.

The guidance puts side effects inside createEffect and resource disposal inside onCleanup, including timers, subscriptions, and event listeners. It also recommends small components, PascalCase filenames, and Vite CSS Modules for local styles. The scope is SolidJS 1.x: SolidJS 2.0 is described as experimental, so this rule should not be used as guidance for its APIs.

What it helps with

  • preserve reactive component props
  • choose state primitives for local and nested data
  • place side effects inside createEffect
  • dispose of timers and subscriptions with onCleanup
  • separate declared and pass-through props with splitProps

Example prompts for the agent

  • Review this SolidJS component and remove top-level props destructuring.
  • Use createSignal for local state and createStore for nested data.
  • Find side effects in *.jsx files and add cleanup with onCleanup.

How to install solidjs

Manually: put the files in place

.cursor/rules/solidjs.mdc

Frontmatter: description, globs, alwaysApply

Step by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules

Questions about solidjs

How do I install solidjs in Cursor?

Add the rule file at .cursor/rules/solidjs.mdc in your Cursor project.

What does the solidjs rule do?

It sets SolidJS 1.x guidance for reactive props, signals, stores, effects, cleanup, and component organization.

Which files does solidjs cover?

The rule applies to JavaScript and JSX files matching **/*.{js,jsx}.

Source text

solidjs.mdc · CC0-1.0
---
description: This guide provides definitive, opinionated best practices for writing high-performance, maintainable SolidJS applications, focusing on reactivity, component architecture, and common pitfalls.
globs: **/*.{js,jsx}
---
# 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();
    });
    ```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Similar IDE rules

All IDE rules for Frontend and UI →

From the same set240

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor