How themes work
A Poltura theme is code. You edit it in the browser code editor, write sections and blocks against the typed theme SDK, and check it before you publish.
On this page
A theme is a tree of TSX components and JSON files. Sections and blocks are React components declared with defineSection and defineBlock. Page layouts, setting values and texts are JSON files beside them. Merchants arrange sections in the Customizer; you write what the Customizer cannot do.
Build a theme section
Write, check, place and publish a section, in about fifteen minutes.
Theme SDK reference
25 typed modules, from cart to ui.
Open the code editor
Every store can run its own theme code. In the admin, open Online store → Themes and choose Edit in code editor on a theme. The Customizer's top bar links to the editor too.
It is a code editor in the browser, with TypeScript completions from the theme SDK, Tailwind hints, format on save, the Theme: New Section… command, a Changes panel and a timeline of earlier saves. Saves go to the theme's draft. Publishing is a separate step, so the live storefront does not change while you work.
A theme imports Poltura APIs from @poltura/theme-sdk/<module>. It may also import its own files, React and a fixed set of libraries (for example @radix-ui/*, lucide-react, motion, clsx and zod). When the check refuses an import, its message lists every allowed package.
The theme tree
theme.json name, schema_version, engine
settings/schema.json generated from src/settings.ts on every save
settings/values.json setting values
settings/presets.json style presets the merchant switches between
pages/<template>.json section layout per template (home, product, ...)
pages/custom/<name>.json layouts of the merchant's custom pages
layout/header.json, footer.json header and footer section groups
emails/<slug>.json email content
texts/<language>.json the theme's own UI texts, one file per language
assets/ images, fonts and icons the theme ships
og/<kind>.tsx share image templates (optional)
src/settings.ts the theme settings panel, written with field.*
src/sections/, src/blocks/ section and block types, one per file
src/layout/ optional chrome in code, such as chat.tsx
src/templates/ optional templates in code, such as pages/landing.tsx
src/components/, src/styles/ shared components and CSS
src/preview-scenarios.ts sample data states for the CustomizerWrite a section
Each file under src/sections/ or src/blocks/ declares its type once. The compiler reads the declarations without running them and builds the Customizer's picker, the editor's layout schema and the publish checks from them. There is no registry file.
import { EditableText, defineSection, field } from '@poltura/theme-sdk/elements';
import { cn } from '@poltura/theme-sdk/ui';
export const uspStrip = defineSection({
type: 'usp-strip',
label: 'USP strip',
icon: 'section',
category: 'marketing',
settings: { text: field.text({ label: 'Text', default: 'Free delivery over 50' }) },
style: ['spacing', 'background', 'typography'],
component: ({ attrs }) => (
<section {...attrs} className={cn(attrs.className, 'py-6')}>
<EditableText setting='text' as='p' className='theme-container text-center' />
</section>
),
});The component spreads attrs onto its one root element. That is how the merchant's style choices reach it. The Build a theme section tutorial walks through the whole flow, from the editor to publishing. Sections, blocks and element styles covers settings, blocks and style capabilities.
Style presets
settings/presets.json lists up to eight named looks of the theme. Each preset has an id, a label, one to five #rgb or #rrggbb swatches for the picker, and a partial set of settings/values.json values:
{
"$schema": "../.schemas/presets.schema.json",
"presets": [
{
"id": "bright",
"label": "Bright",
"label_i18n": { "hu": "Élénk" },
"swatches": ["#ff3d7f", "#111111", "#fafafa"],
"values": { "colors.primary": "#ff3d7f", "colors_dark.primary": "#ff8fb1", "radius": "0" }
}
]
}Switching to a preset changes the draft's settings only. Every look setting takes the preset's value, or else its default (dark_default for a dark twin) from settings/schema.json, so nothing of the previous preset survives. Content settings and every page, layout, text and email stay as they are. Ids use a-z, 0-9 and -, and default is reserved for the shipped look.
A setting's type decides whether it is look or content. Colours, gradients, fonts, selects, radios, numbers, ranges and checkboxes are look. Text, images, links and the rest are content. A setting overrides this with "content": true (a "Show the cookie bar" checkbox is the merchant's decision, not the theme's look) or "content": false (a text field holding a CSS length).
A preset value must name a look setting of settings/schema.json and fit its type. Once a theme ships presets, every look setting needs a default. Saving, installing, publishing and building the theme all refuse a presets file that breaks these rules.
Check before you publish
The code editor runs the theme check on every save and lists its findings in the Problems panel, each with a line and column. It checks layout files, sections, settings, imports and types.
The same check runs as poltura theme check <dir> on a theme directory. --types <pack> points it at the theme SDK's type pack, and --json prints one machine-readable line. The same command line can also pull a theme into a folder, push your edits to a draft, preview, publish and roll back: see Theme commands. Not on npm yet