Each app follows its documentation; every choice it makes is listed, with a link to the page that recommends it, in its bench.json.
- Static HTML control
The reference page with no JavaScript at all. Every interaction is a full page load: forms post and links navigate. It shows what the network and the server cost on their own.
Source · 15 of 15 checks passed
- Vanilla JS control
The reference page plus one small, hand-written module: event delegation, fetch and DOM updates, no library. It stands for the least JavaScript these interactions need.
Source · 2 documented choices · 15 of 15 checks passed
- Alpine.js
alpinejs@3.17.4The reference page with Alpine's directives written into the server's markup. One module, Alpine bundled with the page's stores and components, loads up front and starts Alpine over the server-rendered HTML; stores hold the cart and the filters, and the cart, the quick view and the newsletter call the shared JSON API.
Source · 15 documented choices · 15 of 15 checks passed
- Angular
@angular/core@22.2.0 @angular/ssr@22.2.0The page rendered per request by @angular/ssr's Express server and hydrated with Angular 22's defaults: incremental hydration, with the newsletter below the fold hydrating when it scrolls into view, and event replay. httpResource reads the shared JSON API, whose answers on the server reach the browser in the transfer cache; search and category are query parameters filtered in the browser, and the forms post to Express without JavaScript and to the API once hydrated.
Source · 24 documented choices · 15 of 15 checks passed
- Astro + Preact
astro@7.3.5 @astrojs/preact@6.0.5 preact@10.29.8Astro renders the page on demand with its Node adapter. The header, the catalog, the quick view and the newsletter are Preact islands, each hydrated when it is needed; they share state through nanostores and call Astro Actions, which the same forms also use without JavaScript.
Source · 18 documented choices · 15 of 15 checks passed
- CycleWire
cyclewire@1.2.0+0794a56The reference page activated by CycleWire, built from this repository's source, so every run measures the commit it runs on: the core loads up front, and each interaction's code loads when someone reaches for it. Nothing hydrates.
Source · 7 documented choices · 15 of 15 checks passed
- CycleWire, inline variant
cyclewire@1.2.0+0794a56 esbuild@0.28.2The CycleWire app with the core inlined in the page: CycleWire starts while the page is still parsing, and each action's code loads as one module when someone reaches for it.
A variant of CycleWire: The core is the classic-script build inlined in <head>, which starts from a JSON block that maps actions to URLs, instead of a module loaded with the page; the actions are one module each (esbuild) instead of Vite chunks. It tests the performance guide's advice to inline the core for the fastest activation.
Source · 7 documented choices · 15 of 15 checks passed
- CycleWire, intent only variant
cyclewire@1.2.0+0794a56The CycleWire app with nothing fetched ahead of intent: the core loads up front, and every action's code when someone reaches for it, Add to cart included, on touch screens too.
A variant of CycleWire: No <link rel="modulepreload"> for Add to cart, and start({ preload: 'intent' }), which turns off the look-ahead that fetches the code of the actions in view on touch screens. It shows what the two save an interaction, and what they cost a load.
Source · 8 documented choices · 15 of 15 checks passed
- CycleWire, requests from markup variant
cyclewire@1.2.0+0794a56The CycleWire app with its interactions declared in markup with cyclewire/request, the way htmx builds them: links and forms fetch HTML fragments from the server and swap them in (the results, the header's cart summary, the quick view, the newsletter message), and <cw-stream> messages in an answer update the other parts of the page. The core loads up front, and the request action's code with the page.
A variant of CycleWire: Every interaction is cyclewire/request in the page's markup instead of a hand-written action: links and forms fetch HTML fragments from the server and swap them in, <cw-stream> messages in an answer update the rest of the page, and only the quick view has a small action of its own, to open the dialog once the request has filled it. It shows what CycleWire costs, and how fast it is, used the way htmx is.
Source · 12 documented choices · 15 of 15 checks passed
- Hotwire (Turbo + Stimulus)
@hotwired/turbo@8.0.23 @hotwired/stimulus@3.2.2The reference page from a plain Node server, with Turbo and Stimulus. Without JavaScript its links and forms load full pages; with Turbo, category links are Drive visits, the search results and the quick view load as Turbo Frames, add to cart and the newsletter answer with Turbo Streams, and two Stimulus controllers submit the search as you type and open the quick view dialog.
Source · 21 documented choices · 15 of 15 checks passed
- htmx
htmx.org@2.0.11The reference page from a plain Node server, with htmx attributes on its links and forms. Without JavaScript they load full pages; with htmx they fetch HTML fragments (the results, the quick view, the cart header, the newsletter message) and swap them in.
Source · 14 documented choices · 15 of 15 checks passed
- Marko
marko@6.3.56 @marko/run@0.11.13The page rendered per request by Marko Run's Node server and resumed in the browser by Marko 6, whose one client bundle holds only the code for what can change. A route handler loads the listing and the cart cookie; search and category are template state that toggles the cards' hidden attribute; the forms post to route handlers and are sent with fetch once the page runs; the quick view is a dialog on ?view=, filled from a JSON route when opened in the page.
Source · 23 documented choices · 15 of 15 checks passed
- Next.js
next@16.3.6 react@19.3.0 react-dom@19.3.0App Router Server Components render the page per request from the URL and the cart cookie. Server Actions add to the cart and sign up for the newsletter, typing in the search box updates ?q= for the server to render the results, and the quick view is an intercepted route shown in a modal.
Source · 16 documented choices · 15 of 15 checks passed
- Next.js, client filtering variant
next@16.3.6 react@19.3.0 react-dom@19.3.0The Next.js app with the list filtered in the browser: a Client Component gets the catalog from the page, keeps ?category= and ?q= in the URL with the native History API and reads them with useSearchParams(). The cart, the newsletter and the quick view are the next app's.
A variant of Next.js: A category or a search does not render the page again on the server: one Client Component holds the whole catalog, filters it in the browser, and updates the URL with window.history.pushState() and replaceState(), which Next.js syncs with useSearchParams(). It shows what filtering in the browser saves a filter and a search, and what moving the list into a Client Component changes in a load.
Source · 19 documented choices · 15 of 15 checks passed
- Nuxt
nuxt@4.5.2 vue@3.5.43The page rendered per request by Nuxt's Node server and hydrated by Vue. useFetch loads the products and the cart from Nitro server routes; search and category live in the URL and filter the list in the browser; the forms post to server routes and are sent with $fetch once hydrated; the quick view is a dialog on ?view=, filled with useAsyncData.
Source · 22 documented choices · 15 of 15 checks passed
- Qwik City
@builder.io/qwik@1.20.1 @builder.io/qwik-city@1.20.1The page as a Qwik City app on its Node.js server adapter: rendered per request and resumed in the browser, nothing hydrates. Route loaders read the cart cookie and the URL, the forms are route actions, search and filters are signals, and the quick view fetches its product through server$.
Source · 20 documented choices · 15 of 15 checks passed
- SolidStart
@solidjs/start@2.0.5 solid-js@1.9.15 @solidjs/router@1.0.0The page rendered per request by SolidStart 2 on Nitro's Node server and hydrated by Solid. Queries whose bodies run on the server load the products and the cart; search and category live in the URL and filter the list in the browser; the forms post to server actions and are sent by the router once hydrated, with the new cart in the same response; the quick view is a dialog on ?view=.
Source · 23 documented choices · 15 of 15 checks passed
- SvelteKit
@sveltejs/kit@2.70.3 svelte@5.57.1The page rendered per request by SvelteKit's Node adapter and hydrated by Svelte 5. Search and category live in the URL and filter the list in the browser; add to cart and the newsletter are form actions enhanced with use:enhance; the quick view is a shallow-routed modal.
Source · 15 documented choices · 15 of 15 checks passed