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:

  1. Go to Online Store → Pages
  2. Select a page
  3. Under Theme template, choose the template suffix
  4. Save

Practice Exercise

Create page templates for:

  1. A contact page with form
  2. An FAQ page with accordion
  3. 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

  1. page object provides title, content, handle
  2. JSON templates enable section customization
  3. Alternate templates with .suffix.json naming
  4. Contact forms use {% form 'contact' %}
  5. Blocks make FAQs and content dynamic
  6. Rich text editor content uses .rte class
  7. 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...