mrkeyoor.com_
Wed 23 Sept 00:32 UTC
npmWeb Frontendupdated 22 Sept 2026

solid-js review

solid-js 1.9.15 compiles JSX into DOM creation plus subscriptions that update only the expressions reading changed state. A component function usually runs once; signals are getter functions, and effects track synchronous reads. Our import-all browser build measured 22 KB minified and 8.5 KB gzipped. Version 1.9.15 fixes rejected `lazy()` imports so server Suspense can finish, client hydration can release its pending count, ErrorBoundary receives the error, and a later load may retry. The package also contains stores, resources, context, portals, SSR, and hydration.

Verdict

solid-js 1.9.15 installed in 1 second and produced an 8.5 KB gzipped import-all build in our sandbox, with bundled types and 0 audit findings. Choose it for precise JSX updates if the team will learn accessor-based reactivity and has a plan for the approaching Solid 2.0 migration.

We installed it

Lab card: what happened when we installed solid-jsScreenshot of solid-js documentation
Install✓ · 1s9 packages on disk · 5 MB
ImportESM import works · require() works · ESM package with exports map
Browser8.5 KBgzipped (22 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does solid-js install cleanly?

Yes. In a fresh container with an empty cache, npm install solid-js finished in 1 seconds, leaving 9 packages and 5 MB on disk. npm audit reported no known vulnerabilities.

How much does solid-js add to a browser bundle?

8.5 KB gzipped (22 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does solid-js work with both ESM and CommonJS?

Yes. Both import 'solid-js' and require('solid-js') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does solid-js include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

solid-js or react: which should you use?

react: Choose it when third-party integrations, component inventory, and hiring reach matter more than signal-level updates. solid-js 1.9.15 installed in 1 second and produced an 8.5 KB gzipped import-all build in our sandbox, with bundled types and 0 audit findings.

When should you not use solid-js?

You expect React component bodies and hooks to rerun: Solid components normally set up once, and reading count instead of count() changes the meaning

API stability4/5Solid 1.9.15 preserves the 1.x contracts for signal accessors, one-time component setup, control-flow components, stores, resources, and `solid-js/web` rendering. Its exports map names browser, server, worker, development, CommonJS, and ESM paths. The score stops at 4 because the published Solid 2.0 RC removes or replaces several familiar 1.x APIs, so a new long-lived application should budget for that migration.
Docs5/5The official documentation returned HTTP 200 and covers tutorials, concepts, API references, server rendering, hydration, and framework setup. The README includes JavaScript and TypeScript starters, shows the DOM code produced from JSX, explains which expression updates, specifies compiler settings, and names supported browser and server ranges. It gives readers the render-once model before asking them to memorize primitives.
Maintenance5/5npm published 1.9.15 on August 17, 2026, and GitHub records a push on August 25, 2026. The stable release fixes a rejected `lazy()` import that previously left server Suspense waiting and client hydration pinned, then adds coverage for retry behavior. GitHub currently reports 24 open issues and pull requests combined, the repository is not archived, and 2.0 has reached release-candidate testing.
Ecosystem4/5The npm API counted 3,841,418 downloads for August 18 through August 24, 2026, and GitHub reports 35,920 stars. Solid works with Vite, Babel, TypeScript, Node LTS, recent Deno, Cloudflare Workers, custom renderers, and community packages for primitives and components. React still has more vendor SDKs, design systems, job candidates, and troubleshooting history, which matters for larger organizations.

Use it if

  • You want JSX components with signal-level DOM updates instead of a virtual DOM rerender pass
  • Your interface has frequent local updates and the team is willing to call signal accessors explicitly
  • You need client rendering, streaming SSR, hydration, Suspense, stores, and resources from one core package
  • A compiler-aware Vite or Babel step already fits your frontend toolchain
Skip it if

Setup reality

Our install of solid-js 1.9.15 finished in 1 second. It left 9 packages and 5 MB on disk, with 3 direct dependencies, 0 peer dependencies, and 0 audit findings. The package is ESM with an exports map, while both require() and ESM import worked on Node 22. TypeScript declarations are bundled. Our import-all browser build came to 22 KB minified and 8.5 KB gzipped.

Use a Solid-aware Vite template or add the Solid plugin; installing the runtime does not convert generic JSX into Solid subscriptions. A manual TypeScript setup keeps JSX for the compiler and sets jsxImportSource to solid-js. Imports are split by job: reactivity from solid-js, DOM mounting from solid-js/web, and stores from solid-js/store. Conditional exports choose server implementations under Node, so browser tests and SSR builds need the intended bundler conditions. No credentials or native build are involved.

The first runtime bugs tend to be tracking mistakes. Read a signal as count(), keep reactive prop access inside tracked expressions, use splitProps instead of ordinary destructuring, and remember that reads after an await are outside the effect's synchronous tracking pass. Use For for keyed lists and Index for stable positions in 1.9.15. For SSR, pair server transforms with hydrate() on the client. Rejected lazy imports now reach ErrorBoundary in this version, but each lazy subtree still needs an intentional loading and failure UI.

Patterns

Mount a signal-backed counter render-counter

import { createSignal } from 'solid-js';
import { render } from 'solid-js/web';

function Counter() {
  const [count, setCount] = createSignal(0);
  return (
    <button onClick={() => setCount(n => n + 1)}>
      Count: {count()}
    </button>
  );
}

render(() => <Counter />, document.getElementById('app')!);

`count` is an accessor in Solid 1.9.15. Call `count()` where the current number is required.

Cache a derived total derive-memo

import { createMemo, createSignal } from 'solid-js';

const [items, setItems] = createSignal<Product[]>([]);
const total = createMemo(() =>
  items().reduce((sum, item) => sum + item.price, 0)
);

<p>Total: {total()}</p>

`createMemo` caches its result until a tracked input changes. A plain function is enough when recalculation is cheap.

Run an effect after one signal changes track-explicit-dependency

import { createEffect, createSignal, on } from 'solid-js';

const [query, setQuery] = createSignal('');
createEffect(on(query, (value, previous) => {
  console.log({ previous, value });
}, { defer: true }));

`defer: true` skips the initial call. `on(query, ...)` tracks the named accessor instead of incidental reads in the callback.

Render rows by item identity render-keyed-list

import { For } from 'solid-js';

<ul>
  <For each={todos()} fallback={<li>No tasks</li>}>
    {(todo, index) => <li>{index() + 1}. {todo.title}</li>}
  </For>
</ul>

`For` keys rows by item identity in Solid 1.9.15, and its index argument is an accessor.

Narrow a conditional value render-conditional-value

import { Show } from 'solid-js';

<Show when={currentUser()} fallback={<a href="/login">Sign in</a>}>
  {user => <p>Welcome, {user().name}</p>}
</Show>

The callback receives a reactive accessor for the truthy value. Reading `currentUser()` into an outside constant would capture one moment.

Load a user when its id changes fetch-reactive-resource

import { createResource, createSignal, Suspense } from 'solid-js';

const [userId, setUserId] = createSignal('42');
const [user] = createResource(userId, async id => {
  const response = await fetch(`/api/users/${id}`);
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json();
});

<Suspense fallback={<p>Loading...</p>}>
  <p>{user()?.name}</p>
</Suspense>

`createResource` reruns when `userId` changes in Solid 1.9.15. Solid 2.0 RC replaces this API with async memos.

Change one nested store field update-nested-store

import { createStore } from 'solid-js/store';

const [state, setState] = createStore({
  users: [{ id: 1, name: 'Ada', active: false }]
});
setState('users', user => user.id === 1, 'active', true);

Store values are proxies. Use `setState` paths in Solid 1.9.15 instead of assigning nested properties directly.

Separate local and forwarded props preserve-prop-reactivity

import { splitProps, type ComponentProps } from 'solid-js';

type Props = { label: string } & ComponentProps<'button'>;
function ActionButton(props: Props) {
  const [local, forwarded] = splitProps(props, ['label']);
  return <button {...forwarded}>{local.label}</button>;
}

`splitProps` keeps getter behavior. Ordinary `const { label } = props` may stop later parent updates from reaching the label.

Guard a typed context provide-context

import { createContext, createSignal, useContext, type ParentProps } from 'solid-js';

const ThemeContext = createContext<ReturnType<typeof createSignal<string>>>();
export function ThemeProvider(props: ParentProps) {
  const theme = createSignal('light');
  return <ThemeContext.Provider value={theme}>{props.children}</ThemeContext.Provider>;
}
export function useTheme() {
  const value = useContext(ThemeContext);
  if (!value) throw new Error('ThemeProvider is missing');
  return value;
}

`useContext` returns the context default when no provider exists. With no default here, the helper throws for misplaced consumers.

Remove window listeners on disposal clean-up-listeners

import { createSignal, onCleanup, onMount } from 'solid-js';

function OnlineStatus() {
  const [online, setOnline] = createSignal(navigator.onLine);
  onMount(() => {
    const update = () => setOnline(navigator.onLine);
    window.addEventListener('online', update);
    window.addEventListener('offline', update);
    onCleanup(() => {
      window.removeEventListener('online', update);
      window.removeEventListener('offline', update);
    });
  });
  return <span>{online() ? 'Online' : 'Offline'}</span>;
}

`onCleanup` runs when its owning component scope is disposed. Register cleanup beside the subscription it releases.

Load a component behind Suspense lazy-load-component

import { lazy, Suspense } from 'solid-js';

const Reports = lazy(() => import('./Reports'));

<Suspense fallback={<p>Loading reports...</p>}>
  <Reports />
</Suspense>

Version 1.9.15 forwards a rejected lazy import to ErrorBoundary and permits retry; the imported module must default-export a component.

Reset a failed component subtree recover-render-error

import { ErrorBoundary } from 'solid-js';

<ErrorBoundary fallback={(error, reset) => (
  <section>
    <p>{error.message}</p>
    <button onClick={reset}>Try again</button>
  </section>
)}>
  <Dashboard />
</ErrorBoundary>

ErrorBoundary catches failures in its descendant render and reactive scopes. Unrelated event-handler errors still need their own handling.

Alternatives

PackageRegistryPick it when
reactnpmChoose it when third-party integrations, component inventory, and hiring reach matter more than signal-level updates
vuenpmChoose it for fine-grained reactivity with official routing, state, and single-file component conventions
sveltenpmChoose it when the team prefers compiler-driven single-file components and Svelte 5 runes over JSX accessors

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.