Guide 12 · Developer docs

Performance, caching, deployment and go-live

How Core dna caches pages, what makes a render fast, how code reaches a site and what stays behind, the go-live checklist, and running a site once it is live. For developers, leads and site operators who already build pages in a theme repository.

L2–L3
Unit 02 · Making pages fast

Bundle size

How much JavaScript and CSS each type of page should carry, measured from a production store's committed build for its landing pages, checkout and member area, and the two techniques, entry splitting and dynamic imports, that keep each page to its own share.

Who this is for. Theme and JavaScript developers who own a site's build. Afterwards you can set a weight budget per page type, see which page types break it, and split the build so each page carries only its own code.

Every byte of JavaScript is downloaded, parsed and run before the page can respond, and on a mid-range phone the running costs more than the downloading. Bundle size is therefore a per-page question: what does this type of page need? A build that ships the checkout's code to the home page makes every visitor pay for a feature few of them use. How a theme build is set up is in Builds, bundles and script tags and JavaScript; this page is about the weight.

What a production store ships

A production store on Core dna, measured from its committed build in October 2026, minified and then gzipped as it travels (1 KB = 1,000 bytes):

Loaded onFileMinifiedGzipped
Every pagejQuery90 KB31 KB
components bundle74 KB25 KB
integrations bundle10 KB3 KB
ui bundle6 KB2 KB
layout bundle6 KB2 KB
styles.css159 KB27 KB
fonts.css6 KB1 KB
Checkout onlycheckout bundle402 KB94 KB
checkout.css36 KB7 KB
vendor.css24 KB5 KB
Form pages onlyforms bundle340 KB79 KB
Member areamy-account bundle449 KB181 KB
courses bundle454 KB183 KB
On demandtwo dynamic chunks9 KB and 1 KB2 KB and 1 KB

So a landing page carries about 63 KB of JavaScript and 28 KB of CSS, and the checkout about 157 KB and 41 KB. The checkout's code is larger than everything every page loads, and because it is a separate entry loaded only by the checkout template, no other page pays for it. That one decision is worth more than any amount of minification. The member area is the counter-example: two large bundles on every member page, about 428 KB of JavaScript in all.

A budget per page type

Set the budget before the build grows, per type of page, gzipped:

Page typeJavaScriptCSSThe store above
Landing and content pagesunder 100 KBunder 50 KB63 KB and 28 KB
Checkoutunder 200 KBunder 50 KB157 KB and 41 KB
Member pagesunder 200 KBunder 50 KB428 KB and 28 KB: over

The first two rows are the targets the JavaScript page of the theme guide sets in its section on bundle budgets, which also shows how to make the build fail when an entry exceeds them; a member area is an application and gets the checkout's allowance, no more. A budget the build enforces would have stopped the member area's bundles growing unnoticed. Count third-party scripts separately (Fonts and third-party scripts): they are not in your build, and they are often heavier than it.

Entry splitting: one bundle per page type

Give each page type whose code is large and its own an entry of its own, and have only that type's template load it. The base template loads what every page needs; the checkout template adds the checkout's bundle to the base template's scripts block:

Template code templates/checkouts/single-page.html
{extends 'includes/frontend/base.html'}
{block name="scripts" append}
  <script nonce="{$_inlineScriptNonce}" src="/static/js/checkout.bundle.js?v={$_currentSite->getLastVcsHash()}" defer></script>
{/block}
{% extends 'includes/frontend/base.html' %}
{% block scripts %}
  {{ parent() }}
  <script nonce="{{ _inlineScriptNonce }}" src="/static/js/checkout.bundle.js?v={{ _currentSite.getLastVcsHash() }}" defer></script>
{% endblock %}

Use your own base template and block name. A split is worth it when the code is large and used by few pages: a checkout, a form library, a map, an interactive configurator. It is not worth it for a few kilobytes used everywhere, where one shared file that the browser keeps for a year is better than many small ones (theme files).

Dynamic imports: code that loads when it is used

Inside one page type, code that only some visits need, such as a payment provider's library or a lightbox opened by a click, can be a dynamic import(). The build turns it into a separate chunk that downloads when that code first runs, so a visitor who never opens the lightbox never downloads it; the store above loads its payment providers this way. The chunk is served from the site's own origin, so the default content security policy, which allows the site's own scripts, allows it. Setting it up is in the theme guide's JavaScript page, under loading heavy code on demand, and in Cut a bundle in half with dynamic imports.

Measuring a page's weight

  • Per page: the browser's Network panel, filtered to JS and then CSS, with the transferred column showing gzipped bytes. Do it signed out, for one URL of each page type, and signed in for the member area.
  • Per file: gzip -c dist/app.js | wc -c on the build output gives the size that travels.
  • Unused code: the developer tools' Coverage panel shows how much of each file the page actually ran. A large unused share on a page type is the signal to split.

Load scripts with defer unless they must run before the page renders, so that downloading them does not hold up the first paint; stylesheets are needed for the first paint and belong in the head.

Go deeper

Was this page helpful?

Your answer goes to the docs team.