Rubik Combined Listings

Performance Rubik Combined Listings Swatch

← Back to documentation

Summary

When a storefront app slows a store down, it is usually for one of three reasons: heavy bundles, an API call to the app's server on every page view, or global CSS that fights the theme. Rubik Combined Listings Swatch was built to avoid all three. This page lists what the app actually loads, how it loads it, where the time goes, and how to measure it on your own store. The numbers about the app on this page come from measurements, not estimates, and the page says where and when the main ones were taken.

23 KBcompressed JavaScript for the main script (96 KB uncompressed)
8 KBcompressed CSS, applied only inside the swatch element
~3 msof JavaScript work to render a product-page swatch row
0requests to our servers to render swatches
0render-blocking resources, cookies, or third-party scripts
~36 KBcompressed for everything the app loads on a typical page (the 40 KB carousel library loads only where a carousel layout is used)

Built for Shopify. The app holds Shopify's Built for Shopify badge. Among its requirements, Shopify states that an app "must not reduce the storefront Lighthouse performance score by more than ten points" (requirements), and it measures the app's own admin against Core Web Vitals thresholds (Largest Contentful Paint 2.5 s, Cumulative Layout Shift 0.1, Interaction to Next Paint 200 ms, at the 75th percentile).

What loads on a page

Measured on 26 August 2026 on the public demo store (combinedlistings.rubikdemo.com, Horizon theme), reading the browser's resource timing for the assets served by Shopify's CDN. "Compressed" is the Brotli size Shopify's CDN serves (Brotli and gzip are the two compression formats in common use; for the main files gzip is 6 to 7% larger); the two inline figures are Brotli too. None of these files is render-blocking, meaning none has to finish downloading before the browser can draw the page.

Asset Compressed Uncompressed How it loads
Main script (rubik-combined-listings.min.js) 23.3 KB 96 KB defer: downloads in parallel with the page, runs after the HTML is parsed, never blocks rendering
Stylesheet (rubik-combined-listings.css) 8.1 KB 33.6 KB Downloaded in the background by a small inline script and applied only inside the swatch element, so it is not a page stylesheet and cannot hold up the theme's first paint
Theme configuration (theme_<theme>.js) under 5 KB (most themes under 1 KB; Horizon, the largest, 4.8 KB) 0.8 to 17 KB Only the file for the store's own theme; the other 175 theme files are never requested
Carousel library (rubik-swiper-bundle.min.js) 40.2 KB 155 KB Loaded only where a carousel layout is used: on product pages whose groups use it, as an async tag; elsewhere, fetched on demand the first time a carousel renders. Never on the critical path. Namespaced (RubikSwiper) so it does not overwrite a theme's own Swiper global
Inline embed (settings, theme map, icon font, inline start-up script) ~5 KB 12.2 KB Part of the page HTML; not a separate request
Swatch data ~2 KB (one group of five products) 17 KB Part of the product page HTML that Shopify renders with the page; not a separate request

Everything the app loads on a typical page adds up to about 36 KB compressed (main script, stylesheet, theme configuration). The 40 KB carousel library is added only on pages that render a carousel layout.

What does not load: no framework, no jQuery, no polyfills, no external web fonts (the carousel arrows use a 2 KB icon font embedded in the page), no third-party scripts, no tracking pixels, and it sets no cookies. The main script is plain JavaScript with a single dependency, the carousel library above.

How the app loads

  1. Shopify renders the app embed at the end of <body>. None of the app's code is in the <head> of the served HTML, so the theme's critical CSS, fonts, and hero images are always discovered first.
  2. A small inline script prepares the ground. It reads the store's settings (already in the HTML, rendered from a Shopify app metafield), works out which theme the store runs from a theme map that ships with the embed, starts downloading the stylesheet in the background, and requests the one theme configuration file the store needs. Measured at under half a millisecond.
  3. Three downloads run in parallel: main script, stylesheet, theme configuration (plus the carousel library on pages that use a carousel layout). None is render-blocking.
  4. The main script runs after the HTML is parsed (defer). It parses the swatch JSON already in the page, finds the injection point for the store's theme, and builds the swatch row inside a shadow root (a walled-off part of the page whose styles the theme's CSS does not override). It applies the stylesheet from step 2 in the same step: normally it has already arrived, and if not, the row stays hidden until it does, so there is never a flash of unstyled content. Then it inserts the finished element into the page in a single operation.
  5. Swatch images load last, at the right size. Once the row exists, its images load lazily, and each one carries a srcset ladder with a sizes hint so every browser fetches the smallest file that is still sharp at its own pixel density (details under Rendering choices below).

Step 4 is the app's rendering work, and it measures about 3 ms end to end (2.6 ms in the July 2026 measurement on our development store, Horizon product page). The largest parts are building the swatch elements (0.9 ms), resolving the group's visual settings (0.2 ms), and parsing the JSON (0.1 ms); selector matching and insertion take well under 0.1 ms each. Browsers flag tasks over 50 ms as "long tasks" that hurt interaction responsiveness; the app's rendering is less than a tenth of that.

Data comes from Shopify, not from us

A common performance mistake in storefront apps is fetching data from the app's own server on every page view: an extra connection to a server that may be far from the shopper, and a hard dependency on that server being up. Rubik Combined Listings Swatch has none of that.

Where the time actually goes

In August 2026 we measured the cold-load path on the demo store's Horizon product page, hard-reloaded so no app asset is cached, timing the moment the swatch row becomes visible. The store is on the app's default settings, with every Performance optimization turned off, which is how most stores run. Across repeated runs the swatch row appeared a median of about 1,280 ms after navigation start on this image-heavy page. Absolute timings scale with the shopper's connection and the store's own weight; what stays constant, and what the app is built around, is how that time divides:

Share Phase Whose time it is
~11% (0 to ~145 ms) The store's own HTML arriving and being parsed. Because the app sits at the very end of the document, the browser cannot begin fetching the app's assets until the theme's HTML and the app embeds ahead of it have been received. The store's page weight
~88% (~145 to ~1,275 ms) Fetching the app's assets. The main script and the single theme-configuration file download in parallel, and the render waits for both. Almost none of this is size: the script is 23 KB and its download took about 30 ms. Nearly all of it is request queueing, about 1,050 ms on this page, the app's low-priority assets waiting their turn behind the roughly 250 theme and product images the page also loads. Browser scheduling and network
~0.2% (~1,275 to ~1,278 ms) Everything the app computes: parse, match, build, insert. The app

That queue is the one part a store can shorten. Turning on the optional High priority swatch loading setting moved the app's assets toward the front of the queue: measured on the same page, the wait dropped from about 1,050 ms to about 250 ms and the swatch row appeared at about 550 ms instead of 1,280 ms, with no change to the bytes downloaded. On a warm reload, with every asset served from the browser's cache, the row appears within about 20 ms of the browser reaching the app embed. Two conclusions shaped the app. Shrinking the bundle further would barely move any of these numbers, because the bytes are already a small, fast download and the wait is queueing, not size. The one lever that helps is request priority, which is why the app offers the opt-in High priority swatch loading setting, marked Recommended, rather than forcing it on every store.

Rendering choices that protect Core Web Vitals

Per-theme configuration instead of one bundle for all themes

The app ships placement rules for nearly 200 theme listings and page builders; the rest of the more than 350 themes it supports (supported themes) use the default selectors built into the main script, or app blocks. Those rules are 176 separate configuration files, about 1.4 KB each on average (many themes share one), and a store loads at most one of them: the theme is resolved in the inline script from a map that ships with the embed, so the right file is requested immediately rather than after the main script runs. Shipping every configuration to every store would add about 250 KB uncompressed to each page view, of which a store would use a single file.

Built to fail safely

The app is designed so that a bad network or a stalled request does not turn it into a page-weight problem.

Optional speed settings

These are off by default because each one is a trade-off that depends on the store. You will find them under Settings > Performance optimizations in the app; all but Prerender are marked Recommended there.

Setting What it does When to enable it
High priority swatch loading Adds fetchpriority="high" to the main script and theme configuration so the browser schedules them alongside critical resources instead of behind gallery images. Targets the request queueing we measured on the demo store's product page: about 1,050 ms with the setting off, about 250 ms with it on. Stores whose product pages load many images or apps and where swatches appear noticeably after the price.
Prefetch linked product on hover and Prerender linked product on hover Warms the linked product page when a shopper hovers or touches a swatch, so the navigation after the click is faster (Prefetch) or near-instant (Prerender). Stores where shoppers browse many colors of one product. Prerender is Chromium-only, so keep Prefetch on as well to give other browsers a speed-up.
Preserve scroll position on swatch click Restores the shopper's scroll position after navigating between the products in a group, so the swatch row stays where their thumb was. Mobile-heavy stores with long product pages.

Measure it yourself

  1. Lighthouse or PageSpeed Insights before and after. Run PageSpeed Insights on a product page, then disable the app embed (Online Store > Themes > Customize > App embeds) and run it again. Run each variant several times; single runs can differ by several points on their own. Check the render-blocking resources audit: the app's assets never appear there, because nothing it loads is render-blocking. For reference, on 26 August 2026 the demo store's product page scored 98 on the desktop preset of Lighthouse 13 with the app active (Largest Contentful Paint 1.0 s, Total Blocking Time 0 ms, Cumulative Layout Shift 0.03, from a theme text block), and the app's main script accounted for about 50 ms of main-thread time in that run.
  2. Network panel. In your browser's developer tools, filter the Network panel by rubik. You will see the main script, the stylesheet, and the theme configuration (theme_<theme>.js, served from the same path), plus the carousel library only on pages with a carousel layout, with roughly the compressed sizes shown above (the panel adds response headers), all from Shopify's CDN (cdn.shopify.com). The demo store runs on the app's default settings, with the Performance optimizations off, so the main script loads at normal priority; enabling High priority swatch loading adds fetchpriority="high" to it.
  3. Performance panel. Record a page load and look for the app's script in the main-thread timeline. The swatch rendering itself is a few milliseconds (2.6 ms in the July 2026 measurement on our development store); the main script and the carousel library each appear as their own evaluation task.
  4. Debug mode. Add ?rcl_debug=true to any product or collection URL and open the console. The app logs what it detected (template, theme, configuration), any cards it excluded, and each swatch row it injected; enable console timestamps to see when.

Frequently asked questions

Will the app slow down my store?

It is built not to, and the measured numbers bear that out: about 36 KB of compressed assets on a typical page (76 KB where a carousel layout is used), none of them render-blocking, swatch data rendered into the page by Shopify, and about 3 ms of rendering work. Built for Shopify certification requires that an app does not reduce the storefront Lighthouse performance score by more than ten points.

Does the app call an external server when a shopper loads a page?

No. Swatches render with no request to our servers: the assets come from Shopify's CDN and the swatch data comes from the page itself or from your store's own domain. The one thing the storefront code sends anywhere is a fire-and-forget click-analytics beacon, posted to your own domain after a click and relayed by Shopify's app proxy. If our servers were unreachable, swatches would keep working unchanged; only click analytics would pause. (With the optional Prerender linked product on hover setting on, the browser also loads the linked product page early, scripts included.)

Does it affect LCP, CLS, or INP?

It is designed to stay out of their way. Scripts are deferred or async and placed at the end of the body, so they do not block the Largest Contentful Paint. The app's styles are isolated in a shadow root, and swatch images are lazy-loaded and served at the right size for each screen. The only layout shifts it introduces are the swatch rows themselves being inserted (once on a product page as soon as the deferred script and the theme configuration have run, and per product card on collection pages when that card's data is available), a small adjustment when a row limited to one line collapses its overflow into a "+n" button, and the shift from hiding any theme elements you ask us to hide. The rendering work is a few milliseconds, far below the 50 ms long-task threshold that affects interaction responsiveness.

How much JavaScript does the app load?

23 KB compressed for the main script, plus the configuration for your theme (under 5 KB) and about 5 KB of inline code that is part of the page HTML. A 40 KB compressed carousel library loads only on pages that render a carousel layout.

Does it load on checkout or account pages?

Not on checkout: Shopify runs no theme app embeds there, and the same is true of the new customer accounts pages. The app embed is part of your Online Store theme, so its small script loads with theme pages (product, collection, home, search, cart, blog, and content pages) and does its work only where there is a product or product cards to enhance; elsewhere it finds nothing to do. When the app is turned off in its settings, the embed ships no script or stylesheet; only the small data block of any app block you added to your theme remains until you remove that block.

Does the app set cookies or track shoppers on my storefront?

Not on your storefront: the storefront code sets no cookies, loads no third-party scripts, and stores nothing in the browser by default; the optional scroll position feature uses sessionStorage for a single page transition. Swatch click analytics send aggregated click counts (placement, meaning product page or product card, product ID and title, option name and value, group, whether the product was in stock, swatch position and type, device type) to your store's own domain up to five seconds after a click, with no shopper identifiers in the payload. Cookies used by the app's admin interface are described in the privacy policy.

What if my store is already slow?

The measurements on this page show that swatches appear when the page's own HTML and images allow them to; on heavy stores the app is waiting on the page, not the other way round. The High priority swatch loading setting helps the app's assets get ahead of non-critical images, and the Prefetch and Prerender options make moving between the products in a group faster even when the pages themselves are heavy.

Related guides