Content Pages and 404 Beginner 10 min read
Page Templates: Standard and Custom
Learn how to create and customize page templates in Shopify, including the page object, alternate templates, and merchant-configurable sections.
Pages in Shopify are for static content like About Us, Contact, FAQ, and policies. Understanding page templates helps you create flexible, merchant-editable content experiences.
The Page Object
When viewing a page, the page object is available:
{{ page.title }} {# "About Us" #}{{ page.handle }} {# "about-us" #}{{ page.content }} {# Page HTML content #}{{ page.url }} {# "/pages/about-us" #}{{ page.author }} {# Author name #}{{ page.template_suffix }} {# Custom template suffix #}Basic Page Template
{# templates/page.liquid #}
<div class="page"> <div class="page__container container"> <header class="page__header"> <h1 class="page__title">{{ page.title }}</h1> </header>
<div class="page__content rte"> {{ page.content }} </div> </div></div>JSON Page Template (Online Store 2.0)
{# templates/page.json #}{ "sections": { "main": { "type": "page-content", "settings": { "show_title": true, "container_width": "medium" } } }, "order": ["main"]}Page Content Section
{# sections/page-content.liquid #}
<div class="page-content section-{{ section.id }}"> <div class="page-content__container container container--{{ section.settings.container_width }}"> {%- if section.settings.show_title -%} <h1 class="page-content__title">{{ page.title }}</h1> {%- endif -%}
{%- if page.content != blank -%} <div class="page-content__body rte"> {{ page.content }} </div> {%- endif -%} </div></div>
{% schema %}{ "name": "Page Content", "settings": [ { "type": "checkbox", "id": "show_title", "label": "Show page title", "default": true }, { "type": "select", "id": "container_width", "label": "Container width", "options": [ { "value": "narrow", "label": "Narrow" }, { "value": "medium", "label": "Medium" }, { "value": "wide", "label": "Wide" } ], "default": "medium" } ]}{% endschema %}Alternate Page Templates
Create specialized templates for different page types:
Contact Page Template
{# templates/page.contact.json #}{ "sections": { "main": { "type": "page-content", "settings": { "container_width": "narrow" } }, "contact-form": { "type": "contact-form", "settings": {} }, "contact-info": { "type": "contact-info", "settings": {} } }, "order": ["main", "contact-form", "contact-info"]}FAQ Page Template
{# templates/page.faq.json #}{ "sections": { "main": { "type": "page-content", "settings": { "show_title": true } }, "faq": { "type": "faq-accordion", "blocks": [] } }, "order": ["main", "faq"]}Landing Page Template
{# templates/page.landing.json #}{ "sections": { "hero": { "type": "hero-banner", "settings": {} }, "features": { "type": "feature-grid", "settings": {} }, "testimonials": { "type": "testimonials", "settings": {} }, "cta": { "type": "call-to-action", "settings": {} } }, "order": ["hero", "features", "testimonials", "cta"]}Contact Form Section
{# sections/contact-form.liquid #}
<section class="contact-form section-{{ section.id }}"> <div class="container container--narrow"> {%- if section.settings.heading != blank -%} <h2 class="contact-form__heading">{{ section.settings.heading }}</h2> {%- endif -%}
{%- form 'contact', class: 'contact-form__form' -%} {%- if form.posted_successfully? -%} <div class="contact-form__success" role="status"> <p>{{ section.settings.success_message }}</p> </div> {%- endif -%}
{%- if form.errors -%} <div class="contact-form__errors" role="alert"> {{ form.errors | default_errors }} </div> {%- endif -%}
<div class="contact-form__fields"> <div class="contact-form__field"> <label for="contact-name">Name</label> <input type="text" id="contact-name" name="contact[name]" value="{{ form.name }}" required > </div>
<div class="contact-form__field"> <label for="contact-email">Email</label> <input type="email" id="contact-email" name="contact[email]" value="{{ form.email }}" required > </div>
<div class="contact-form__field"> <label for="contact-phone">Phone (optional)</label> <input type="tel" id="contact-phone" name="contact[phone]" value="{{ form.phone }}" > </div>
<div class="contact-form__field contact-form__field--full"> <label for="contact-message">Message</label> <textarea id="contact-message" name="contact[body]" rows="5" required >{{ form.body }}</textarea> </div> </div>
<button type="submit" class="contact-form__submit button button--primary"> {{ section.settings.button_text | default: 'Send Message' }} </button> {%- endform -%} </div></section>
{% schema %}{ "name": "Contact Form", "settings": [ { "type": "text", "id": "heading", "label": "Heading", "default": "Get in Touch" }, { "type": "text", "id": "button_text", "label": "Button text", "default": "Send Message" }, { "type": "text", "id": "success_message", "label": "Success message", "default": "Thanks for contacting us! We'll get back to you soon." } ], "presets": [ { "name": "Contact Form" } ]}{% endschema %}FAQ Accordion Section
{# sections/faq-accordion.liquid #}
<section class="faq-section section-{{ section.id }}"> <div class="container"> {%- if section.settings.heading != blank -%} <h2 class="faq-section__heading">{{ section.settings.heading }}</h2> {%- endif -%}
{%- if section.blocks.size > 0 -%} <div class="faq-accordion"> {%- for block in section.blocks -%} <details class="faq-item" {{ block.shopify_attributes }}> <summary class="faq-item__question"> {{ block.settings.question }} <span class="faq-item__icon">+</span> </summary> <div class="faq-item__answer rte"> {{ block.settings.answer }} </div> </details> {%- endfor -%} </div> {%- else -%} <p>Add FAQ items using the theme editor.</p> {%- endif -%} </div></section>
{% schema %}{ "name": "FAQ Accordion", "settings": [ { "type": "text", "id": "heading", "label": "Heading", "default": "Frequently Asked Questions" } ], "blocks": [ { "type": "faq_item", "name": "FAQ Item", "settings": [ { "type": "text", "id": "question", "label": "Question", "default": "What is your return policy?" }, { "type": "richtext", "id": "answer", "label": "Answer", "default": "<p>We offer a 30-day return policy on all items.</p>" } ] } ], "presets": [ { "name": "FAQ Accordion", "blocks": [ { "type": "faq_item" }, { "type": "faq_item" } ] } ]}{% endschema %}Page Styles
.page-content { padding: var(--spacing-2xl) 0;}
.page-content__title { font-size: clamp(2rem, 5vw, 3rem); margin: 0 0 var(--spacing-xl); text-align: center;}
.page-content__body { max-width: 800px; margin: 0 auto;}
/* Container widths */.container--narrow { max-width: 600px;}
.container--medium { max-width: 900px;}
.container--wide { max-width: 1200px;}
/* Contact form */.contact-form__fields { display: grid; gap: var(--spacing-md);}
@media (min-width: 768px) { .contact-form__fields { grid-template-columns: 1fr 1fr; }
.contact-form__field--full { grid-column: span 2; }}
.contact-form__field label { display: block; font-size: 0.875rem; font-weight: 500; margin-bottom: var(--spacing-xs);}
.contact-form__field input,.contact-form__field textarea { width: 100%; padding: var(--spacing-sm); border: 1px solid var(--color-border); border-radius: var(--border-radius);}
.contact-form__submit { margin-top: var(--spacing-lg);}
.contact-form__success { padding: var(--spacing-md); background: #d1fae5; color: #065f46; border-radius: var(--border-radius); margin-bottom: var(--spacing-lg);}
/* FAQ accordion */.faq-accordion { max-width: 800px; margin: 0 auto;}
.faq-item { border-bottom: 1px solid var(--color-border);}
.faq-item__question { display: flex; justify-content: space-between; align-items: center; padding: var(--spacing-md) 0; font-weight: 500; cursor: pointer; list-style: none;}
.faq-item__question::-webkit-details-marker { display: none;}
.faq-item__icon { font-size: 1.25rem; transition: transform 0.2s;}
.faq-item[open] .faq-item__icon { transform: rotate(45deg);}
.faq-item__answer { padding-bottom: var(--spacing-md); color: var(--color-text-light);}Assigning Templates to Pages
In Shopify Admin:
- Go to Online Store → Pages
- Select a page
- Under Theme template, choose the template suffix
- Save
Practice Exercise
Create page templates for:
- A contact page with form
- An FAQ page with accordion
- A team/about page with blocks for team members
Test by:
- Creating pages in the admin
- Assigning different templates
- Customizing via theme editor
Key Takeaways
pageobject provides title, content, handle- JSON templates enable section customization
- Alternate templates with
.suffix.jsonnaming - Contact forms use
{% form 'contact' %} - Blocks make FAQs and content dynamic
- Rich text editor content uses
.rteclass - Container widths via section settings
What’s Next?
The next lesson covers Reusable Content Sections for building flexible content blocks.
Finished this lesson?
Mark it complete to track your progress.
Discussion
Loading comments...