Skip to Content

Solid

Solid TSX rules from eslint-plugin-solid.

Solid components compile to fine-grained reactivity, so patterns that look correct in React (destructuring props, calling useEffect-style hooks with array deps) silently break reactivity in Solid.

This family captures the common Solid-only pitfalls.

Source: eslint-plugin-solid (MIT).

Rule index

Each rule name links to the detailed section below.

Examples come from the checked lint corpus  or package-level rule tests when project layout matters.

Rules

solid/components-return-once

Reject early and conditional return from a Solid component, Solid components must return exactly once at the top level.

Example:

import { Component } from "solid-js"; const App: Component<{ name: string }> = (props) => { // reports: solid/components-return-once (error) if (!props.name) return <span />; return <strong>{props.name}</strong>; };

solid/event-handlers

Require DOM event handler props to use canonical Solid casing (onClick, not onclick / onClIcK) so the compiler recognizes them as events.

Also flags on*-named props bound to non-function values, which look like handlers but are not.

Example:

import { createSignal } from "solid-js"; createSignal(0); // reports: solid/event-handlers (error) const tree = <button onclick="save">save</button>;

solid/imports

Route each Solid export to the correct entry point (solid-js, solid-js/web, or solid-js/store).

Autofixable. A specifier that stands alone has its declaration’s module specifier rewritten; one with siblings is cut out and either appended to an existing import from the correct entry or given a synthesized one.

The diagnostic names the symbol and the entry point it belongs to (Import `render` from `solid-js/web`.). The fix relocates the specifier rather than dragging its neighbours along: import { render } from "solid-js" has its module specifier rewritten in place, while import { createSignal, render } from "solid-js" keeps createSignal where it belongs and moves render out — into an existing import from the correct entry when the file has one, otherwise into a synthesized declaration above. A type-only declaration stays type-only on both ends.

Example:

// reports: solid/imports (error) import { createEffect, render } from "solid-js"; // reports: solid/imports (error) import { createStore } from "solid-js/web";

solid/jsx-no-duplicate-props

Reject duplicate JSX props on the same Solid element. Unlike React, Solid silently keeps the first value, so the duplicate is dead code and almost always a typo.

Example:

import { createSignal } from "solid-js"; createSignal(0); // reports: solid/jsx-no-duplicate-props (error) const tree = <div id="a" id="b" />;

solid/jsx-no-script-url

Reject javascript: URLs in Solid JSX attributes (href, src, …), they evaluate the suffix as code in the page context and are a long-standing XSS vector.

Example:

import { createSignal } from "solid-js"; createSignal(0); // reports: solid/jsx-no-script-url (error) const tree = <a href="javascript:alert(1)">click</a>;

solid/jsx-no-undef

Reject Solid JSX component names that are not declared or imported in scope.

Example:

import { createSignal } from "solid-js"; createSignal(0); // reports: solid/jsx-no-undef (error) const tree = <Missing />;

solid/no-array-handlers

Reject array values passed as Solid event handlers, Solid does not unwrap the array form React supports.

Example:

import { createSignal } from "solid-js"; const [enabled] = createSignal(false); // reports: solid/no-array-handlers (error) const tree = <button onClick={[enabled, () => enabled()]}>x</button>;

solid/no-destructure

Reject destructured Solid component props, destructuring breaks reactivity by reading the property eagerly.

Example:

import { Component } from "solid-js"; // reports: solid/no-destructure (error) const Hello: Component<{ name: string }> = ({ name }) => <span>{name}</span>;

solid/no-innerhtml

Reject innerHTML JSX attributes because they bypass sanitization and are a common XSS sink. A static string literal is still allowed by default; flip allowStatic off to ban that form too.

Example:

import { createSignal } from "solid-js"; const [html] = createSignal("<b>x</b>"); // reports: solid/no-innerhtml (error) const tree = <div innerHTML={html()} />;

solid/no-proxy-apis

Reject Solid APIs that rely on ES6 Proxy (including new Proxy, Proxy.revocable, imports from solid-js/store, and dynamic spread shapes through mergeProps).

For shipping to runtimes without Proxy support; off by default.

Example:

import { createSignal } from "solid-js"; createSignal(0); // reports: solid/no-proxy-apis (error) const handler = new Proxy({}, {});

solid/no-react-deps

Reject React-style dependency arrays in Solid tracked scopes (createEffect(() => ..., [deps])).

Type-aware via the Checker. The tag below claims the array is dead, so the callee has to be the Solid primitive itself rather than a same-named local helper or a shadowing parameter, and only symbol resolution can say that. Enabling this rule puts the whole run on the checker path.

The finding is tagged Unnecessary, so an editor greys the array out: Solid tracks dependencies automatically, the array is inert, and the reported range is the array literal alone. Deleting exactly what is faded is the whole resolution.

Example:

import { createEffect } from "solid-js"; // reports: solid/no-react-deps (error) createEffect(() => {}, []);

solid/no-react-specific-props

Reject React-specific JSX props such as className and htmlFor, Solid uses class and for.

className and htmlFor are autofixed: the rename is 1:1 and only the name token is rewritten, so the value survives untouched. A string, an expression container, or no value at all stays intact. The key arm stays diagnostic-only. A Solid DOM element does not consume key, so its resolution is a deletion rather than a rename, and the deletion has to take the surrounding whitespace with it to leave valid JSX.

That split is also why the rule carries no diagnostic tag. Unnecessary is read once per rule, so tagging it for the key arm would fade the two findings that only need renaming.

Example:

import { createSignal } from "solid-js"; createSignal(0); // reports: solid/no-react-specific-props (error) const tree = <label className="primary" htmlFor="field" key="save" />;

solid/no-unknown-namespaces

Restrict namespaced JSX attributes (ns:name={...}) to the built-in Solid namespaces (on:, oncapture:, use:, prop:, attr:, bool:, style:, class:).

Extra names can be allowed through the allowedNamespaces option.

Example:

import { createSignal } from "solid-js"; createSignal(0); // reports: solid/no-unknown-namespaces (error) const tree = <div custom:active="true" />;

solid/prefer-classlist

Rewrite class={cn({ ... })} / clsx(...) / classnames(...) calls to the reactive classlist={{ ... }} prop.

Deprecated and off by default upstream.

Example:

import { createSignal } from "solid-js"; const clsx = (input: Record<string, unknown>) => Object.keys(input).join(" "); const [enabled] = createSignal(true); // reports: solid/prefer-classlist (error) const tree = <div class={clsx({ active: enabled() })} />;

solid/prefer-for

Replace inline array.map(item => <JSX />) with Solid’s <For> component so the iteration stays keyed and reactive instead of re-creating every child on each update.

Example:

import { createSignal } from "solid-js"; const [items] = createSignal([1, 2, 3]); // reports: solid/prefer-for (error) const tree = ( <section> {items().map((item) => ( <span>{item}</span> ))} </section> );

solid/prefer-show

Rewrite {cond && <JSX />} short-circuits in JSX to <Show when={cond}>...</Show>. Stylistic only, Solid’s compiler already handles the boolean form, so it is off by default.

Example:

import { createSignal } from "solid-js"; const [enabled] = createSignal(true); // reports: solid/prefer-show (error) const tree = <section>{enabled() && <strong>Ready</strong>}</section>;

solid/reactivity

Reject common Solid reactivity breakages, reading a signal outside a tracking scope, destructuring a Store, etc.

Example:

import { createEffect, createSignal } from "solid-js"; function App() { const [count] = createSignal(0); createEffect(async () => count()); // reports: solid/reactivity (error) return <span>{count}</span>; }

solid/self-closing-comp

Collapse JSX elements with no children to the self-closing form (<Foo></Foo> to <Foo />). Configurable per component vs HTML element, including a "void" mode that only enforces it for void tags.

Example:

import type { Component } from "solid-js"; const Icon: Component = () => <svg />; // reports: solid/self-closing-comp (error) const tree = <Icon></Icon>;

solid/style-prop

Require style={{...}} keys to be valid kebab-case CSS properties ("font-size", not React’s fontSize) and dimensioned values to be strings, Solid does not append implicit px.

Example:

import { createSignal } from "solid-js"; createSignal(0); // reports: solid/style-prop (error) const tree = <span style={{ fontSize: "12px" }} />;

solid/validate-jsx-nesting

Reject JSX nestings that the HTML parser would silently restructure at runtime, <p> cannot contain block-level children, <a> cannot contain another <a>, and <button> cannot contain other interactive elements.

Example:

const a = ( <p> {/* reports: solid/validate-jsx-nesting (error) */} <div>block in paragraph</div> </p> );
Last updated on