Build the default layout
Assemble the HTML shell every page shares — head metadata, Open Graph tags, body classes, and the components that wrap your content
A layout is the outer HTML every template renders into. Your theme needs at least one — layouts/default.liquid — and it's where the bits that should appear on every page live: the <head>, global stylesheets, headers, footers.
You'll build it up in pieces.
Step 1 — Create the file
Create layouts/default.liquid and start with a doctype and an empty shell:
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
</body>
</html>Save the file. The dev-server browser tab should reload to a blank-but-valid page.
Step 2 — Add the page metadata
The renderer exposes a handful of globals that resolve to the right values for whatever page is being viewed: page_title, page_description, page_image, body_classes. Use them in the <head> so every page gets its own title, social share image, and meta description without you writing per-page code.
Add this to the <head>:
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ page_title }} — My Theatre</title>
<meta name="description" content="{{ page_description }}">
<meta property="og:title" content="{{ page_title }}">
<meta property="og:description" content="{{ page_description }}">
<meta property="og:image" content="{{ page_image }}">Replace My Theatre with your site's name, or wire it up to a setting later — you'll add a site.name setting on the Variants and theme settings page.
Step 3 — Make space for header injections
Basker injects JSON-LD, the preview banner, generator meta, and similar head-level content via {{ content_for_header }}. Add it as the last line inside <head>:
{{ content_for_header }}Without this, structured data won't render and the preview banner won't show when you're previewing draft content. The renderer warns about its absence in the dev-server logs.
Step 4 — Link a stylesheet
Create a flat assets/main.css for now:
touch assets/main.cssReference it from the layout using the asset_url filter. The stylesheet_tag filter wraps the URL in a <link> element with the right attributes:
{{ 'main.css' | asset_url | stylesheet_tag }}Add that line right above {{ content_for_header }}.
The path you pass to asset_url is relative to your assets/ folder. Nested layouts work too ('css/main.css'), but for the tutorial keep things flat — easier to debug, and Basker's CDN doesn't care either way.
Step 5 — Wire up the body
Now the body. Add a body_classes variable to the opening tag — the renderer fills it with classes derived from the resolved template name, which is useful for per-template CSS hooks like .template-page or .template-event.
<body class="{{ body_classes }}">
</body>Step 6 — Render the global header and footer
Most themes split their global UI into reusable components and pull them in with {% render %}. Create two files:
components/global-header.liquid:
<header class="site-header">
<a href="/" class="site-header__logo">My Theatre</a>
<nav class="site-header__nav">
<a href="/about">About</a>
<a href="/events">What's on</a>
<a href="/contact">Contact</a>
</nav>
</header>components/global-footer.liquid:
<footer class="site-footer">
<p>© {{ 'now' | date: '%Y' }} My Theatre</p>
</footer>Then render them from the layout, with the page content sandwiched in between:
<body class="{{ body_classes }}">
{% render 'components/global-header' %}
<main class="page">
{{ content_for_layout }}
</main>
{% render 'components/global-footer' %}
</body>{{ content_for_layout }} is where the rendered template body lands. Without it, only the layout's own markup would show.
components/ and snippets/ are both resolved by the {% render %} tag, so the path prefix tells the renderer which folder to look in. The convention is components/ for larger page-level pieces (header, footer, navigation) and snippets/ for smaller reusable bits (icons, formatters). Use whichever fits the piece you're building.
Step 7 — Final layout
Your layouts/default.liquid should now look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ page_title }} — My Theatre</title>
<meta name="description" content="{{ page_description }}">
<meta property="og:title" content="{{ page_title }}">
<meta property="og:description" content="{{ page_description }}">
<meta property="og:image" content="{{ page_image }}">
{{ 'main.css' | asset_url | stylesheet_tag }}
{{ content_for_header }}
</head>
<body class="{{ body_classes }}">
{% render 'components/global-header' %}
<main class="page">
{{ content_for_layout }}
</main>
{% render 'components/global-footer' %}
</body>
</html>What's next
The layout is ready, but {{ content_for_layout }} is empty until you add a template that writes into it. That's the next page: Add page templates.
Going deeper
- Layouts — alternate layouts,
{% layout %}overrides, validation rules. - Liquid tags — full reference for
render,asset_url,stylesheet_tag, and the rest. - Template context — every global available in a layout or template.