The theme manifest
How Basker generates a structured manifest from your theme files during upload, and what drives the editor experience
The theme manifest is the structured representation of your theme that Basker generates automatically during upload. Every block schema, template schema, and settings file in your theme is parsed, validated, and consolidated into a single manifest object that drives the editor experience.
Why this matters
When you upload a theme ZIP, Basker doesn't simply store your files. It reads every .liquid file and settings_schema.json, extracts the structured data from {% schema %} tags, validates the content, and produces a manifest. This manifest is the single source of truth that the editor uses to:
- Present the correct fields for each block and template.
- Determine which blocks are available on which templates.
- Apply theme-wide settings to the live site renderer.
- Detect changes between theme versions for deployment tracking.
If you understand the manifest, you can predict exactly how your theme will behave in the admin. You can also diagnose problems that surface only after upload — missing blocks in the editor, incorrect field types — by reasoning about what's in the manifest.
What the manifest is
The manifest is a JSON object with three top-level properties. It's not a file you create; Basker generates it from your theme files during upload.
type ThemeManifest = {
blocks: ThemeBlockSchema[];
settings: JsonObject[] | null;
templates: ThemeTemplateManifestEntry[];
};Key characteristics:
- Auto-generated. You never write or edit the manifest directly. It's produced during upload.
- Deterministic. Given the same theme files, the same manifest is always produced.
- Versioned. Each upload sets a
manifestRevisiontimestamp. - Diffable. When a theme is re-uploaded, Basker computes a diff between the previous and new manifest. Added, removed, and changed blocks, templates, and settings are recorded in the deployment log.
How the manifest is built
The upload process follows a fixed sequence.
1. ZIP extraction
The uploaded ZIP is extracted in memory. __MACOSX/ and examples/ directories are discarded. Every remaining file is categorised by its path prefix: blocks/, layouts/, templates/, assets/, snippets/, or config/.
2. Schema extraction from Liquid files
Every .liquid file is parsed through a Liquid engine configured for schema extraction. The engine registers passthrough implementations for tags like {% render %}, {% layout %}, and {% stageblocks %} so it can parse without resolving external references. When it encounters {% schema %}, it captures the raw text inside.
If the Liquid syntax is invalid, a compatibility error is recorded. If the schema JSON is malformed, a schema validation error is recorded. Either way, the file is still processed; it simply contributes no manifest entry.
Only the first {% schema %} tag in a file is used. Multiples produce a warning; the rest are ignored.
3. Block manifest entries
For each .liquid file inside blocks/, the extracted schema JSON is parsed into a block manifest entry:
type ThemeBlockSchema = {
name: string;
settings: ThemeField[];
label?: string;
singular?: string;
plural?: string;
fileHash?: string;
};name is required. It's the unique identifier for the block across the entire theme. If label, singular, or plural are missing, they default to name.
Field types within settings are normalised during extraction. richtext becomes richText, textarea becomes textArea, datetime becomes dateTime. Unrecognised types are preserved but produce a validation warning.
A SHA-256 hash of the entire file content is computed and stored as fileHash. This enables change detection: when a theme is re-uploaded, Basker compares hashes to determine which blocks have actually changed.
4. Template manifest entries
For each .liquid file inside templates/, the extracted schema is parsed into a template manifest entry:
type ThemeTemplateManifestEntry = {
template: string;
collection: string;
name: string;
settings: ThemeField[];
allowed_blocks: JsonValue[];
fileHash?: string;
};template, collection, and name are derived from the filename, not from the schema JSON.
Filename parsing:
| Filename | collection | name | template |
|---|---|---|---|
page.liquid | page | default | page.liquid |
page.landing.liquid | page | landing | page.landing.liquid |
event.sidebar.liquid | event | sidebar | event.sidebar.liquid |
index.liquid | index | default | index.liquid |
page.liquid | page | default | page.liquid |
settings comes from the schema JSON. allowed_blocks comes from the blocks key. Each entry in allowed_blocks should match the name of a block defined in blocks/. A referenced block name that doesn't exist in the theme produces a validation warning.
5. Settings extraction
If config/settings_schema.json exists, its contents are parsed as a JSON array of setting groups. The first group holds theme metadata (name, version, author) and has no fields. Every configurable group sets "type": "group" and lists its controls in a fields array, where each field's starting value comes from default.
[
{
"name": "theme_settings",
"theme_name": "My Theme",
"theme_version": "1.0.0",
"theme_author": "Your Studio"
},
{
"name": "colours",
"type": "group",
"fields": [
{
"type": "color",
"name": "primary_colour",
"label": "Primary colour",
"default": "#1a1a2e"
}
]
},
{
"name": "typography",
"type": "group",
"fields": [
{
"type": "select",
"name": "heading_font",
"label": "Heading font",
"options": [
{ "label": "Sans-serif", "value": "sans-serif" },
{ "label": "Serif", "value": "serif" }
],
"default": "sans-serif"
}
]
}
]If the file is absent, settings is null. If the file is present but contains invalid JSON or a non-array value, settings is null and a warning is recorded.
File hashing for change detection
Every block and template file is hashed using SHA-256 from the full file content (HTML, Liquid tags, schema). The hash is stored in fileHash.
When a theme is re-uploaded, Basker compares hashes between the previous and new manifests to determine:
- Added entries — present in the new manifest, absent from the previous.
- Removed entries — present in the previous, absent from the new.
- Changed entries — present in both with different content (detected via hash or deep comparison of the schema object).
This comparison powers the deployment diff that's recorded in the theme deployments log whenever an active theme is re-uploaded.
Manifest revision tracking
Each upload sets a manifestRevision property on the theme document — an ISO 8601 timestamp marking when the manifest was generated:
"2025-03-15T14:22:08.421Z"It's not a version number; it's a point-in-time marker. It changes with every upload, regardless of whether the manifest content actually changed. Use the deployment diff to determine real changes.
How the manifest drives the editor
After the manifest is generated and stored, the editor synchronises it with the underlying admin schema. For each entry in manifest.blocks, a corresponding admin field group is created or updated. The block's settings array is transformed into editor controls — what site editors see when adding the block to a page.
The same applies to templates: each entry in manifest.templates becomes a set of editable fields in the page editor when that template is selected.
Blocks and templates that existed in the previous manifest but are absent in the new one are removed from the editor. This ensures the admin always reflects the current state of the theme.
Tradeoffs
| Approach | Advantages | Disadvantages | Best when |
|---|---|---|---|
Single settings_schema.json for all theme settings | One file to maintain; clear separation from templates | Can grow large for complex themes | Theme has fewer than 20 setting fields |
| Settings distributed across template schemas | Co-located with the template they affect | Harder to get a full picture of all settings | Template-specific settings that don't apply globally |
| Both combined | Maximum flexibility | Two mechanisms to understand | Complex themes with both global and per-template settings |
Recommendation: Use config/settings_schema.json for global theme settings (colours, fonts, layout preferences) and template-level {% schema %} settings for options specific to a single template.
In practice
You maintain a theme with 8 blocks, 5 templates, and a settings_schema.json. You rename a block from hero_banner to hero_section in its schema, update the blocks array in two templates that reference it, and add a new colour setting to settings_schema.json.
After uploading, the deployment diff records:
- Blocks:
hero_bannerremoved,hero_sectionadded (the rename is treated as a new block becausenamechanged). - Templates: Two templates changed — their
allowed_blocksarrays differ. - Settings: Changed (new colour field detected).
The editor deletes the old hero_banner block definition and creates the new hero_section. Pages that had the old block will no longer render it — this is why block name changes need to be coordinated with content migration.
Common misconceptions
| Misconception | Reality |
|---|---|
| The manifest is a file I create in my theme | The manifest is auto-generated during upload. You define schemas in .liquid files and settings_schema.json; Basker consolidates them. |
| Changing the HTML in a block file doesn't affect the manifest | The fileHash changes whenever any part of the file changes. The editor only re-syncs fields when the schema content differs. |
manifestRevision tracks the number of uploads | It's an ISO 8601 timestamp, not a counter. |
| Re-uploading an unchanged theme has no effect | A new manifestRevision is set and the upload is processed. With matching hashes, the deployment diff shows no meaningful changes. |
| Block names can be changed freely | Renaming is treated as removing the old block and adding a new one. Existing page content referencing the old name loses its association. |
Related concepts
- Theme validation — The upload process validates all files before generating the manifest. Validation errors and warnings are recorded in a validation log alongside the manifest. See Validation errors.
- Deployment diffs — When an active theme is re-uploaded, a diff between the old and new manifests is stored. See Versioning and rollback.
Related
- Tutorial — hands-on introduction.
- Theme directory structure — what feeds into the manifest.