Solid 2.0 client · LilScript port

Solid,
smaller.

Same Solid 2.0 client, compiled through LilScript. The size that matters is one real app: official js-framework-benchmark. LSX is the JSX layer. Why it is smaller →

npm install @itslil/solidjs
js-framework-benchmark · Brotli-11

Solid 2.0 keyed table vs @itslil/solidjs

JFB gzip-9
JFB CPU · same app
JFB raw JS
select · same app

JFB is one Vite vendor chunk plus the keyed table — the way a Solid app ships. Closed-world demos below delete unused runtime per file and overstate the gap.

js-framework-benchmark · Chrome 151

CPU versus
Solid 2.0.

Official keyed jumbotron, 15 blocks, CPU throttling. Same table: Solid JSX vs LSX, both cloneNode templates. Both sides read selected() on every row — nine same-app workloads, not a createSelector shortcut.

Workload Solid 2.0 @itslil/solidjs Ratio

Why smaller

Not a harder
minifier.

Three things get mixed together in size arguments: whether the client still behaves like Solid 2.0, how that graph is spelled in JS, and what a bundler is allowed to delete. The fair number is one Vite app — official js-framework-benchmark — not a reconstructed vendor and not these demos added up.

Same flush, same pending

The port is the 2.0 authoring surface: microtask flush, split effects, For / Show / Reveal / Loading, stores, createProjection, action / createOptimistic, isPending / latest, hydrate. The new Async 2.0 pairs are the same JSX on the left and LSX on the right. Official JFB still calls render() on both sides — that row is the same keyed table, not a cut-down port.

Owned fields become slots

The JFB gap is not a meaner minifier. Solid 2.0 already shortens internal keys at publish time. Extra Terser property-mangle on the keyed app is a couple hundred Brotli bytes. The rest is representation: Solid’s graph is objects (e.se, e.Ne after ^_). LilScript structs lower to slots (e[0], e[1]). extern class names like textContent stay exact. Terser cannot prove ownership.

Tree-shaking is the fair size

Solid sizes a tree-shaken app (sideEffects: false, pay-for-use). Official JFB is that method: one vendor chunk plus the jumbotron. The Solid keyed bundle does not contain hydrate / createOptimistic / isPending strings; neither does the LSX one. Unfair: export * (~35 kB Brotli), adding published flat web.js, or summing these iframes (that counts Solid once per file). Fair: the JFB row.

Same templates, thinner For

Both jumbotrons compile to cloneNode HTML. Solid JSX does that in @solidjs/web; LSX does it in the lilx lowerer, then keyedEach. The remaining asterisk is proof, not APIs: Lil closed-world DCE can drop more than Vite can prove unused, and Solid keeps promise/createMemo async rails in every Solid build. Those APIs still exist on this package — the lab’s Async 2.0 pairs exercise them.

Tooling

LilScript, LSX, this page

  1. LilScript is the language and compiler. It types the program, then searches JS spellings against Brotli. Owned aggregates can be positional. That compiler lives in yeargun/lilscript — see why it beats minifying harder and mangle / ABI.
  2. LSX (.lilx) is JSX for LilScript. parse-jsx.mjs reads the markup; lower.mjs emits cloneTemplate plus For / Show calls into lsx.lil. Same idea as Solid’s JSX → template().cloneNode(true).
  3. This lab pairs each Solid 2.0 Vite app with the LSX compile of the same UI. The hero and the first evidence row are official JFB (one vendor chunk). The grid is closed-world extras: filters, side-by-side iframes, replay. CPU cards are the krausest harness, not inferred from size.
LSX createSignal flush For Show Switch Reveal Loading Errored createStore createProjection reconcile action createOptimistic hydrate renderToString lazy children isPending latest

Solid 2.0 vs solidlil, live

The complete
client lab.

Left iframe is official Solid 2.0 JSX. Right iframe is the same UI in LSX. Async 2.0 is createMemo + Loading, action / createOptimistic, sequential Reveal, and createProjection — both runtimes, same markup. These pairs are closed-world extras — LilScript can delete unused runtime per file — so do not add the rows. A real Solid app is one vendor chunk plus modules: the JFB keyed table above.

The receipts

Raw, gzip,
and Brotli.

First row is the official keyed table — Vite + terser, gzip-9 / Brotli-11. Demo rows are closed-world LSX extras.

Case Solid raw Lil raw Solid gzip Lil gzip Solid brotli Lil brotli Brotli Δ

How client apps are sized. The js-framework-benchmark keyed table is one Solid 2.0 app as Vite ships it — tree-shaken, one vendor chunk. Summing the demo rows counts Solid’s client once per file; LilScript’s closed-world compile deletes unused runtime per file. That overstates the gap. Use the JFB row.

Why smaller ↗

Zero runtime dependencies

Drop Solid 2.0 into
a smaller box.

npm install @itslil/solidjs