Theme directory structure
Required and optional directories, file naming conventions, and asset size limits for Basker themes
You package themes as ZIP files and upload them to Basker. The directory structure determines how Basker extracts blocks, templates, layouts, settings, and assets into the theme manifest.
| Directory | Required | Purpose |
|---|---|---|
blocks/ | Yes | Reusable content blocks with schema definitions |
layouts/ | Yes | HTML shell templates that wrap page content |
templates/ | Yes | Collection-specific page templates |
assets/ | No | Static files: CSS, JavaScript, images, fonts |
snippets/ | No | Reusable Liquid partials included via {% render %} |
components/ | No | Larger Liquid partials for page-level UI like headers, footers, or card components, also resolved by {% render %} |
config/ | No | Theme-level configuration files |
.well-known/ | No | Files served at /.well-known/* for domain verification, security disclosure, and similar use cases |
Required directories
blocks/
.liquid files defining reusable content blocks. Each must include a {% schema %} tag with a JSON object containing at minimum name (string) and settings (array).
Blocks are available to editors in the admin. The schema defines the fields editors enter when adding a block to a page.
blocks/
hero-banner.liquid
text-section.liquid
image-gallery.liquid
call-to-action.liquidSchema requirements:
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Must match the block's filename without the .liquid extension |
settings | array | Yes | Field definitions for editor inputs |
label | string | No | Display name in the editor. Defaults to name |
singular | string | No | Singular label. Defaults to name |
plural | string | No | Plural label. Defaults to name |
Example block file:
<section class="hero-banner">
<h1>{{ block.heading }}</h1>
<p>{{ block.subheading }}</p>
</section>
{% schema %}
{
"name": "hero-banner",
"label": "Hero Banner",
"singular": "Hero Banner",
"plural": "Hero Banners",
"settings": [
{
"name": "heading",
"label": "Heading",
"type": "text"
},
{
"name": "subheading",
"label": "Subheading",
"type": "textArea"
}
]
}
{% endschema %}Validation rules:
- Missing
{% schema %}produces an error during upload validation. The local theme checker reports it as a warning so you can keep iterating. - Missing
nameproduces an error. settingsmust be an array. A non-array value produces an error.- Duplicate
namevalues across block files produce a warning. - Put exactly one
{% schema %}tag in each block. Multiple schema tags produce a validation warning and do not have a stable merge order.
See Block schema reference for the full field type list.
layouts/
.liquid files defining the outer HTML shell. Every theme must include layouts/default.liquid.
layouts/
default.liquid
checkout.liquid
blank.liquidRequired placeholders:
| Placeholder | Purpose | Placement |
|---|---|---|
{{ content_for_header }} | Basker injects scripts, meta tags, JSON-LD, and preview banners | Inside <head> |
{{ content_for_layout }} | Basker outputs the rendered template body captured by the template | Inside <body> |
Every template must declare its layout explicitly with {% layout 'layouts/default.liquid' %}. There is no implicit default. A template without a {% layout %} tag renders on its own. Basker wraps template bodies in {% capture content_for_layout %}, so an omitted layout tag produces an empty page.
Validation rules:
- Missing
layouts/default.liquidproduces a validation error. It is one of the structural errors that prevents upload. - A
{% layout %}tag that references a non-existent layout file produces a validation error. Basker reports it as a theme-health issue, not a structural upload blocker.
templates/
.liquid files that control how Basker renders content for specific collections. Filenames follow a strict naming convention.
templates/
page.liquid
page.landing.liquid
event.default.liquid
post.default.liquid
blog.default.liquid
index.liquidFile naming convention:
[collection].liquid or [collection].[name].liquid| Segment | Description | Examples |
|---|---|---|
collection | The Basker content collection this template serves | page, event, post, blog, organization, person, venue, work, season, series |
name | The template variant identifier | default, landing, sidebar, full-width |
Basker treats a template with only one segment before .liquid, such as index.liquid or page.liquid, as a base template. In this case, collection is the filename, and name defaults to "default".
Schema structure (optional but recommended):
{% schema %}
{
"settings": [
{
"name": "show_sidebar",
"label": "Show sidebar",
"type": "switch"
}
],
"blocks": ["hero-banner", "text-section"]
}
{% endschema %}| Property | Type | Required | Description |
|---|---|---|---|
settings | array | No | Template-level field definitions |
blocks | array | No | Names of blocks permitted on pages using this template |
blocks array entries must match name values defined in blocks/. A reference to a non-existent block name produces a warning.
Validation rules:
- Missing
{% schema %}produces a warning. - No
index.liquid,page.liquid, ordefault.liquidproduces a structural error that prevents upload. - Invalid JSON in
{% schema %}produces an error. settingsas a non-array value produces a warning.blocksentries referencing undefined block names produce a warning.
Optional directories
assets/
Static files served via the Basker CDN. Reference assets in Liquid using asset_url.
assets/
css/
main.css
typography.css
js/
navigation.js
images/
logo.svg
hero-bg.jpg
fonts/
custom-font.woff2Referencing assets:
<link rel="stylesheet" href="{{ 'css/main.css' | asset_url }}">
<script src="{{ 'js/navigation.js' | asset_url }}"></script>
<img src="{{ 'images/logo.svg' | asset_url }}" alt="Logo">Allowed file extensions:
| Category | Extensions |
|---|---|
| Stylesheets | .css |
| Scripts | .js |
| Data and metadata | .json, .map, .txt, .xml, .webmanifest |
| Images | .gif, .ico, .jpg, .jpeg, .png, .svg, .webp |
| Fonts | .eot, .ttf, .woff, .woff2 |
| Other supported source | .vue |
Files with extensions outside this list produce a validation error. Basker reports this in theme health, not as a structural error that prevents upload.
Size limits:
| File type | Maximum size |
|---|---|
| Images (gif, ico, jpg, jpeg, png, svg, webp) | 1.5 MB |
| All other assets | 2.5 MB |
Exceeding either limit produces a warning.
Validation rules:
- Disallowed file extensions produce an error.
- Assets referenced in Liquid but missing from
assets/produce an error. - Assets present in
assets/but not referenced in any Liquid file produce a warning (orphaned assets).
snippets/
Reusable Liquid partials, included in layouts/templates/blocks via {% render %}.
snippets/
social-links.liquid
breadcrumbs.liquid
pagination.liquidUsage:
{% render 'social-links' %}
{% render 'pagination', total_pages: total_pages, current_page: current_page %}Basker deprecates the {% include %} tag. Use {% render %} instead. Files using {% include %} produce a deprecation warning during validation.
Validation rules:
- Basker resolves a bare-name
{% render %}reference againstsnippets/,components/, andblocks/. It resolves a reference that contains a path separator from the theme root. Anything that resolves to neither produces a warning. - Snippets present in
snippets/but never referenced produce a warning (unused snippets).
components/
A second home for Liquid partials. Use it for larger page-level UI, such as global headers, footers, navigation menus, and card components. snippets/ holds smaller reusable bits, such as icons or formatters.
components/
global-header.liquid
global-footer.liquid
event-card.liquidcomponents/ is a naming convention rather than a separate Liquid root. Render a component with its path from the theme root, such as {% render 'components/global-header' %}. Render files in snippets/ without the directory prefix, such as {% render 'social-links' %}.
config/
Theme-level configuration files.
config/
settings_schema.jsonsettings_schema.json
Defines theme-wide settings that appear in the admin under the theme's settings panel. The file must be a JSON array of setting groups.
[
{
"name": "theme_settings",
"settings": [
{
"name": "primary_colour",
"label": "Primary colour",
"type": "color"
},
{
"name": "font_family",
"label": "Font family",
"type": "select",
"options": [
{ "label": "Sans-serif", "value": "sans-serif" },
{ "label": "Serif", "value": "serif" }
]
}
]
}
]Each entry is a setting group. Each must have a name property.
Validation rules:
- File must parse as valid JSON.
- Top-level value must be an array.
- Non-object entries produce a warning.
- Entries missing
nameproduce a warning.
Tenant locking: A setting group with "name": "theme_settings" may include a "tenant" property. Setting it locks the theme to that tenant's slug. Uploading the theme to any other site produces an error.
.well-known/
Basker serves files in .well-known/ verbatim at /.well-known/<filename> on the live site. Common uses include apple-app-site-association, assetlinks.json, and security.txt.
.well-known/
apple-app-site-association
assetlinks.json
security.txtBasker validates .well-known/ files more strictly than regular assets.
Allowed extensions:
| Extension | Notes |
|---|---|
.json | JSON manifest files |
.pem | PEM-encoded certificates |
.txt | Plain text |
.xml | XML documents |
| (no extension) | Permitted: many .well-known standards use extensionless filenames |
Size limit: 100 KB per file.
Validation rules:
- Disallowed extensions produce an error.
- Files larger than 100 KB produce a warning.
- Keep files directly inside
.well-known/so their public URL is predictable. Basker normalizes and checks paths before serving files.
Ignored directories
| Directory | Reason |
|---|---|
__MACOSX/ | Basker skips this when it unpacks the theme ZIP |
examples/ | Basker excludes this from manifest generation |
Allowed field types
Field types used in settings arrays across blocks, templates, and settings_schema.json.
| Type | Description |
|---|---|
text | Single-line text input |
textArea | Multi-line text input |
richText | Rich text editor with formatting |
number | Numeric input |
checkbox | Boolean checkbox |
switch | Boolean toggle switch |
select | Dropdown selection |
radio | Radio button group |
color | Color picker |
date | Date picker |
dateTime | Date and time picker |
email | Email address input |
url | URL input |
upload | File or image upload |
relationship | Reference to another document |
code | Code editor |
json | JSON editor |
hidden | Hidden field: not shown to editors |
group | Groups nested fields together |
array | Repeatable set of fields |
Type aliases: Basker normalizes richtext to richText, textarea to textArea, and datetime to dateTime. Unsupported type values produce a warning.
Nesting: group and array types support a fields sub-array for nested field definitions. Nesting deeper than four levels produces a best-practice warning.
Complete example
my-theme/
assets/
css/
main.css
variables.css
js/
navigation.js
gallery.js
images/
logo.svg
default-hero.jpg
fonts/
brand-regular.woff2
brand-bold.woff2
blocks/
hero-banner.liquid
text-section.liquid
image-gallery.liquid
call-to-action.liquid
feature-grid.liquid
config/
settings_schema.json
layouts/
default.liquid
checkout.liquid
snippets/
social-links.liquid
breadcrumbs.liquid
pagination.liquid
seo-meta.liquid
templates/
index.liquid
page.liquid
page.landing.liquid
event.default.liquid
post.default.liquid
blog.default.liquidRelated
- Tutorial: apply this structure to a working theme.
- The theme manifest: how Basker transforms directory contents during upload.