Render essential content in Liquid and HTML, not Java Script
Render initial content with Liquid and HTML instead of client-side frameworks. Use server-side rendering (Hydrogen) if frameworks are required, not client-side frameworks on Liquid storefronts.
When a browser receives server-rendered HTML, it can paint content and discover resources immediately. The preload scanner reads through HTML as it arrives and starts fetching images, stylesheets, and fonts before the page finishes loading. This is the fastest path to visible content.
Client-side JavaScript frameworks bypass this entirely. Instead of receiving ready-to-display HTML, the browser receives an empty shell and must complete a sequential chain before the user sees anything:
- Download the HTML document (which contains little or no visible content).
- Parse the HTML and discover the framework's JavaScript bundle.
- Download, parse, and execute the JavaScript bundle.
- The framework makes data-fetching requests (to the Storefront API or other endpoints).
- After data returns, the framework renders content into the DOM.
- Only now can the browser discover images and other resources that the rendered content references.
Each step in this chain depends on the previous step completing. The result is a long sequential waterfall where the browser sits idle, waiting, while the user stares at a blank or skeleton page.
This waterfall directly harms three Core Web Vitals metrics:
- FCP (First Contentful Paint): Nothing renders until the framework finishes executing and inserting content. With server-rendered HTML, the browser paints content as soon as it parses the HTML.
- LCP (Largest Contentful Paint): The LCP image can't be discovered until after JavaScript renders the
imgelement into the DOM. The preload scanner never sees it because it wasn't in the original HTML. - INP (Interaction to Next Paint): Large JavaScript bundles consume main-thread time during parsing and execution. While the main thread is busy with framework code, user interactions (taps, clicks, scrolls) are delayed or unresponsive.
Anchor to Use Liquid for initial page contentUse Liquid for initial page content
Render the initial view of every page with Liquid and HTML. When the server responds with complete HTML, the browser displays content immediately and the preload scanner discovers all referenced resources in the markup.
Before: A client-side framework fetches product data and renders it with JavaScript.
After: Liquid renders the product directly into HTML. The browser displays the product image and text as soon as it parses the response.
In the Liquid version, the product image URL is present in the HTML from the start. The preload scanner discovers it immediately and begins downloading the image in parallel with other page resources. No JavaScript needs to execute first.
Anchor to Layer JavaScript progressivelyLayer Java Script progressively
Start with server-rendered content, then add interactivity on top. The initial HTML works without JavaScript. JavaScript enhances the experience when it loads.
This approach is called progressive enhancement. The server-rendered HTML provides the baseline experience, and JavaScript adds interactive features, such as variant switching, quantity selectors, and add-to-cart behavior.
The JavaScript that enhances this form is small and targeted. It asks the server to re-render the section for the newly selected option values, then swaps in the returned HTML. It doesn't render any of the initial content, and it doesn't need variant data in the page:
Anchor to Use the Section Rendering API for dynamic updatesUse the Section Rendering API for dynamic updates
When content needs to change after the initial page load, use the Section Rendering API to fetch server-rendered HTML fragments instead of building DOM with JavaScript. The server renders the updated section with Liquid and returns the complete HTML. Your JavaScript replaces the existing section content with the server's response.
This keeps your rendering logic in Liquid where it belongs. The server handles the data and the templates. JavaScript handles fetching and swapping the HTML.
This approach provides several advantages over client-side rendering:
- Rendering logic stays in a single place (Liquid templates).
- The server handles data access, so the browser doesn't need API keys or complex data-fetching code.
- The HTML response is ready to display. No parsing, no template compilation, no virtual DOM diffing.
- The JavaScript you ship is minimal: just
fetchand DOM replacement.
For more detail, see Use the Section Rendering API for dynamic updates.
Anchor to ExamplesExamples
Anchor to Example 1: Product page rendered with Liquid instead of JavaScriptExample 1: Product page rendered with Liquid instead of Java Script
Before: JavaScript fetches product data and renders the entire product display.
After: Liquid renders the product. The image and content are in the initial HTML. The option picker uses product.options_with_values, so the page doesn't load every variant, and JavaScript adds option switching through the Section Rendering API.
Anchor to Example 2: Collection gridExample 2: Collection grid
Before: JavaScript fetches the collection and renders a product grid.
After: Liquid renders the grid. Filtering and sorting use the Section Rendering API to fetch updated server-rendered HTML.
When a customer selects a filter, JavaScript fetches the updated grid from the Section Rendering API:
The server handles filtering and returns a fully rendered grid. The browser doesn't need product data, template logic, or a rendering framework.
Anchor to Example 3: Diagnose whether your site relies on JavaScript for renderingExample 3: Diagnose whether your site relies on Java Script for rendering
To check whether essential content depends on JavaScript:
- Open the site in Chrome.
- Open DevTools: Command+Option+I on macOS, or Control+Shift+I on Windows.
- Open the Command Menu: Command+Shift+P on macOS, or Control+Shift+P on Windows.
- Type
Disable JavaScriptand select the option. - Reload the page.
If the page is blank or missing its primary content, such as product images, titles, prices, or navigation, then the site relies on client-side JavaScript for rendering. Move this content to Liquid templates so that it's present in the server-rendered HTML.
Also look for framework-specific patterns that hide content until JavaScript initializes:
x-cloak(Alpine.js) andv-cloak(Vue): These attributes hide elements until the framework initializes. If any are above the fold, then users on slow connections see blank space. Check:document.querySelectorAll('[x-cloak]').- JS-bound image attributes: Alpine's
:loading="..."or Vue'sv-bind:loadingset theloadingattribute dynamically. Without the framework, these images have noloadingattribute, so the preload scanner can't determine their priority. - JS-triggered visibility: Images rendered with
opacity: 0that use JavaScript to fade in delay LCP. The image downloads, but the browser can't record the LCP event until the element is visible.
Anchor to TestingTesting
- Disable JavaScript test: Follow the diagnosis steps in Example 3. Every piece of content visible in the initial viewport should render without JavaScript.
- Performance panel waterfall: Record a performance trace in Chrome DevTools. Compare the time between navigation start and FCP. Server-rendered pages show content appearing early. JavaScript-rendered pages show a long gap where the main thread is busy executing scripts before any content appears.
- Network panel resource discovery: Check when the LCP image request appears in the Network panel waterfall. On a server-rendered page, it appears early, because the preload scanner discovers it during HTML parsing. On a JavaScript-rendered page, it appears late, after the framework bundle finishes executing.
- Compare bundle sizes: Check the total JavaScript payload in the Network panel. Server-rendered pages with progressive enhancement typically ship far less JavaScript than pages that depend on a client-side framework for rendering.
Anchor to ReferencesReferences
- Debugging common causes for slow loading in Shopify Liquid storefronts
- 3 ways to find your worst JavaScript offenders for page load
- Use the Section Rendering API for dynamic updates
- Avoid over-fetching product variants
- Support high-variant products
- Performance in Hydrogen
- Understanding INP
- Audit and remove third-party scripts
- Section Rendering API reference