Developer docs  /  Themes

How a request becomes a page

The public storefront is mounted under /t (ThemeV2PublicEndpoints.cs doesMapGroup("/t")), with the organisation resolved from the request host. GET /t/{**path} landsin GetThemeV2Page.Endpoint, which whitelists the query string (location, category,attributes, embed) and calls IThemeV2RenderService.RenderPathAsync. Because the storefrontlives under /t, every internal link a template emits must be /t/... — that is what thestorefront_url filter is for (see §4.4).

The flow inside RenderPathAsync:

  1. Route resolveThemeV2RouteResolver.Resolve(path) maps the URL to a ThemeV2RouteResult: a template path, a data scope, an optional slug/location slug, and an IsEmbed flag. Paths starting with api, assets, account, checkout, favicon.ico, robots.txt or sitemap.xml are excluded up front (they belong to the API / Angular SPA).
  2. Org theme lookup — the active OrganisationThemeV2 row is loaded (and lazily auto-provisioned against the base theme if the org predates ThemeV2). This row carries the all-important CacheVersion token.
  3. Rendered-page cache check — if a non-draft render of this exact org + path + version + filter combination is cached, it is returned immediately.
  4. File resolutionLoadResolvedFilesAsync builds one dictionary of every theme file (base overlaid by org overrides — see §6), stripping {% schema %} blocks from .fluid files and pulling section schemas out for default hydration. Binary assets are skipped; only .css/.js assets flow through because the layout inlines them.
  5. Context buildBuildRenderContextAsync runs the queries for the route's data scope and produces the ThemeV2RenderContext (§4). Section settings are hydrated against the schemas (ThemeV2SectionHydrator) so missing values pick up schema defaults and empty block lists are seeded from defaultBlocks — the storefront and the builder inspector always agree.
  6. Page render — the route's template is parsed (cached) and rendered with the context. If the template file doesn't exist and the route is a Page scope matching a stored CMS page, templates/page.fluid is used; otherwise templates/404.fluid.
  7. Layout render — the page HTML becomes content_for_layout on a ThemeV2LayoutContext, along with the inlined assets/theme.css and assets/theme.js, and the layout renders around it. Embed requests prefer layouts/embed.fluid, falling back to layouts/main.fluid (which is mandatory — the renderer throws without it).
  8. Cache write — the full HTML is cached (non-draft renders only).

Rendering is defensive against tenant-authored templates: MaxRecursion = 100,MaxSteps = 100_000, and a 10-second wall-clock render timeout.