Folders
A theme has a fixed set of folders. Only these are readable and writable by the CLI, the code editor, and the visual editor:
Subfolders inside these are not supported.
assets/img/logo.png will not sync, keep asset files flat in assets/.
Root files
The four kinds of file
Layout
A layout is the complete HTML document. It receives the rendered template astemplateContent and is responsible for the <head>, the global components (navbar, footer, and so on), and the script tags.
layouts/master.njk
settings.json (templates.<key>.layout), so a theme can ship more than one, but in practice master covers everything.
Template
Templates hold the page body only. Most of them are nothing but the component loop, because the actual content is composed by the merchant in the visual editor:templates/shop.njk
components_order and the component definitions come from settings.json for the current template.
Component
A component is one addable section. The filename must match the component type key inschema.json, so components/hero.njk pairs with a hero entry in schema.components.
Components read their own configuration from properties and their instance ID from componentId:
components/text-block.njk
A component does not inherit variables set by whatever rendered it. It receives its own
properties, the page data, and the globals. See scope rules.Snippet
A snippet is a reusable partial that takes keyword arguments. Snippets are not visible in the editor and have no schema entry..njk extension is required in the snippet name. Document a snippet’s arguments in a comment at the top, the way the official themes do:
snippets/section-header.njk
Required files
For a page to render, three things must exist:- An entry for the template key in
settings.jsonundertemplates, with alayoutvalue. - The layout that entry names, in
layouts/. - The template file itself, in
templates/.
Component not found or Snippet not found in place, which is usually your first clue that a filename and a schema key disagree.
Templates and routes
Routing is not filename-based. SellAuth resolves each URL to a template name, then renderstemplates/<name>.njk.
/terms redirects to /terms-of-service. The current template name is always available as templateName, which is how layouts special-case pages:
Legal pages
The seven legal pages share one route pattern and all render throughtemplates/legal-page.njk, with legal_page telling the template which one it is. See Template Variables for its shape. A legal page without content returns 404, unless a custom page uses that slug.
A theme copy without a
legal-page entry in settings.json (every shop copy made before the template shipped, and community themes) keeps rendering terms, privacy and refunds through the older terms, privacy-policy and refund-policy templates, and returns 404 for the four newer pages until the merchant updates the theme. Updating removes the three older templates from the copy. If you maintain a custom theme, add templates/legal-page.njk and its settings.json entry yourself.Cookie consent
Every official layout renderssnippets/cookie-consent.njk just before the js.cookie and Alpine script tags, so window.saConsent exists before the theme script and before snippets/script-integrations.njk. The snippet renders nothing unless shop.settings.cookie_consent.enabled is true and the page is not the builder or a preview. Scripts that must wait for consent are written as <script type="text/plain" data-consent="functional|analytics|marketing">; the runtime turns them into live scripts when the category is allowed. The theme script checks window.saConsent.has('marketing') before storing an affiliate code and has('analytics') before storing UTM attribution, and subscribes with onChange so a later acceptance still captures them. Any link to #cookie-settings reopens the preferences. See the Cookie Consent guide for the merchant-facing behaviour.
Custom pages
Merchants can create extra pages with their own slug and their own component composition. All of them render through the single filetemplates/custom-page.njk.
Each custom page gets its own key in settings.json (custom-page-1712345678901) holding its name, slug, layout, and components. At render time:
templateNameis the settings key, for examplecustom-page-1712345678901.custom_pageholdskey,name, andpath.
custom_page.path if a specific page needs special markup:
templates/custom-page.njk
^[a-z0-9-]+$ and cannot collide with a built-in route. These slugs are reserved:
Custom pages require a qualifying plan. On plans without them, custom page URLs return 404.