Decide where the pages of a Core dna site are composed before writing code: on the platform, in your own application, or on the platform with a few pieces filled in by the browser. The decision tree, each shape in depth, multi-brand and languages, and a table and case studies to make the call.
L2
Unit 04 · Hybrid, the common case
Handing state to the browser
Hybrid, the common casePage 14 of 29Updated
Render the state the browser needs into the page once, as a window object or a JSON island, and treat it as authoritative instead of fetching it again. The template in both engines, the module that reads it, and the three rules that keep it safe.
Who this is for. The JavaScript developer on a hybrid site, and the theme developer who writes the page the JavaScript starts from. After this page you can hand server state to a script without a second request, safely, in either engine.
The pattern
When the browser needs data the server already has, write it into the page while rendering, and let the script start from it. A single-page checkout is the usual case: the template has the checkout, its currency, its total and its items, and the script needs all four before it can draw anything. Fetching them again after the page loads costs a request on every page view and a moment where the page shows nothing.
The checkout page is a safe place for this because it is never cached: the platform's checkout controller switches the page cache off for it. The template, checkouts/single-page.html, receives the checkout as checkout:
The total arrives as a decimal string, such as "139.00", which is how the platform keeps money exact; convert it only for display. The script that reads the handoff takes a copy once and owns it from then on:
JavaScriptsrc/js/checkout/state.js
// The server's handoff is the starting state; nothing re-fetches it on load.
const state = structuredClone(window.checkout);
export function getState() {
return state;
}
export function replaceItems(items) {
// After a change, take the platform's answer as the new truth.
state.items = items;
}
Three rules
Every value goes through json_encode, in Smarty and in Twig, never string concatenation inside quotes. It escapes the quotes, backslashes and the / of a closing </script> that would otherwise break out of the script. A name with an apostrophe in it is the usual way this fails in production.
Carry the nonce. The site's Content Security Policy refuses an inline script without nonce="{$_inlineScriptNonce}"; the browser console shows the refusal.
Only on a page that is not cached. A cached page was rendered for one visitor and is sent to the next, so hand over only what is the same for everyone. Anything per visitor on a cached page arrives in a fragment.
_csrfToken exists only when someone is signed in. A guest at checkout has no token, and needs none: the platform checks the token only on writes made with a signed-in session. Carrying it is the subject of the next page: One client module, carrying the CSRF token.
A JSON island instead of a global
A page with several independent scripts can keep their data out of the global scope with a JSON block per script. It is data rather than code, so the Content Security Policy does not apply to it and it needs no nonce:
The same handoff carries configuration that differs by environment rather than by visitor, which is safe on a cached page because every visitor of that environment gets the same values. One Twig site in the case studies hands its scripts the addresses of the back-end services they call this way, with one set of addresses for production and another for development.