Theme Customer Accounts
Stores can render customer account pages (login, registration, order
history, addresses, password reset) directly from the active theme instead
of the built-in account pages. When the feature is on and the theme ships
the account templates, the storefront serves them inside the normal theme
layout — same header, footer, fonts, and settings as every other page.
Enabling theme account pages
Merchants enable the feature in the admin under
Settings → Preferences → Customer accounts by choosing Theme account
pages.
The fallback is automatic and per-page:
- Feature off (default) → the built-in account pages render, exactly as
before. Themes without account templates need no changes.
- Feature on, theme ships
templates/customers/* → the theme’s
templates render.
- Feature on, theme lacks a given template → the storefront serves
the built-in page for that route automatically. Nothing breaks.
Theme developers should ship the full template set (below) so merchants get
a consistent experience when they switch the toggle on.
Template set
Account templates live under templates/customers/ and follow the same
rules as every other template: they can be JSON section templates
(.json, composing sections) or plain Aqua/Liquid templates
(.aqua / .liquid). Either form is rendered inside the theme layout.
| Template | Renders |
|---|
customers/login | Login form + password recovery form |
customers/register | Account registration |
customers/account | Account dashboard: order history + addresses summary |
customers/order | Single order detail |
customers/addresses | Address book (list, create, edit, delete) |
customers/activate_account | Set a password from an account-invite email link |
customers/reset_password | Set a new password from a reset email link |
Routes
| Route | Template | Auth |
|---|
/account | customers/account | Required |
/account/login | customers/login | Guests only |
/account/register | customers/register | Guests only |
/account/addresses | customers/addresses | Required |
/account/orders/{id} | customers/order | Required |
/account/activate/{customer_id}/{token} | customers/activate_account | Token from email |
/account/reset/{customer_id}/{token} | customers/reset_password | Token from email |
/account/logout (GET) | — logs the customer out, redirects to / | — |
/account/recover (POST) | — password-recovery submission target | — |
Redirect behavior is enforced by the platform — themes don’t need their own
guards:
- A logged-out customer requesting
/account, /account/addresses, or
/account/orders/{id} is redirected to /account/login.
- A logged-in customer requesting
/account/login or
/account/register is redirected to /account.
GET /account/recover redirects to /account/login#recover — themes
conventionally use the #recover fragment to reveal the recovery form.
Use the routes object rather than hard-coding paths:
routes.account_url, routes.account_login_url,
routes.account_logout_url, routes.account_register_url,
routes.account_addresses_url, routes.account_recover_url.
All account forms use the standard {% form %} tag. The
tag renders the <form> element with the correct action URL and method and
injects any required hidden inputs — themes only supply the visible fields.
customer_login
Posts email + password. On failure the page re-renders with
form.errors set.
create_customer
Registration. Posts to /account; on success the customer is logged in and
redirected to /account. On failure the register page re-renders with
form.errors, and the submitted first_name, last_name, and email
values are echoed back on the form object so inputs can keep their values.
recover_customer_password
Password-recovery request. Takes a bare email input (no customer[...]
prefix). To prevent account enumeration, the platform always reports
success — form.posted_successfully? is true after any submission, so
show a neutral “check your email” message.
The reset email links the customer to
/account/reset/{customer_id}/{token}, which renders
customers/reset_password.
reset_customer_password / activate_customer_password
Set a new password from a reset link, or a first password from an invite
link. Both take customer[password] and customer[password_confirmation].
The identifying token from the URL is injected automatically as hidden
inputs by the {% form %} tag — themes add nothing.
On success the customer is logged in and redirected to /account.
customer_address
Create or edit an address. Pass customer.new_address to create, or an
existing address to edit — the form tag picks the right action URL either
way.
Inside a customer_address form:
| Property | Description |
|---|
form.id | The address id being edited (new for the create form) — useful for element ids |
form.set_as_default_checkbox | Renders the “set as default address” checkbox input |
form.first_name, form.address1, form.country, … | Current values of the address being edited |
Notes:
address[country] submits the full country name (that’s what the
option tags emit as values).
address[default] = 1 marks the address as default — this is what
form.set_as_default_checkbox renders.
customer_address forms never return field errors — the submission
always redirects back to /account/addresses, and
form.posted_successfully? is always true for this form type.
Deleting an address is a POST to the address URL with a _method=delete
override. A plain form works:
Account pages also ship a small platform helper script that provides the
classic POST-link helper (synthesizes exactly this form + _method
submission from JavaScript) and the country/province selector — existing
theme JavaScript that constructs these helpers keeps working unmodified.
See Helper JavaScript.
| Property | Description |
|---|
form.errors | Error codes from the last submission of this form type (empty otherwise) |
form.errors.messages[code] | Default English message for a code |
form.errors.translated_fields[code] | Human label for the field the code refers to |
form.posted_successfully? | true when this form type was just submitted successfully |
form.errors is iterable (yields error codes such as email, password,
or the generic form) and supports contains:
For a one-liner, pipe through default_errors to render a ready-made
error list:
A failed login sets the single generic form error code (with a default
message like “Incorrect email or password.”) — no hint is given about
whether the email exists.
Objects
customer
Available on all account pages (and globally on every page when the
customer is logged in — {% if customer %} works in headers as usual).
| Property | Type | Description |
|---|
customer.id | string | Customer ID |
customer.name | string | Full name |
customer.first_name / customer.last_name | string | Name parts |
customer.email | string | Email address |
customer.phone | string | Phone number |
customer.accepts_marketing | boolean | Marketing opt-in |
customer.has_account | boolean | Always true for logged-in customers |
customer.orders | array | Order history (paginate with {% paginate %}) |
customer.orders_count | number | Total orders |
customer.total_spent | number | Lifetime spend, in the currency’s minor units (pipe through money) |
customer.addresses | array | All saved addresses |
customer.addresses_count | number | Number of saved addresses |
customer.default_address | object | The default address |
customer.new_address | object | Empty address stub for the create form |
customer.tags | array | Customer tags |
customer.metafields | object | Custom metafields |
order
Available as order on customers/order, and as each entry of
customer.orders on customers/account.
| Property | Type | Description |
|---|
order.name | string | Display name, e.g. #1001 |
order.order_number | number | Numeric order number |
order.customer_url | string | URL of the order’s account page (/account/orders/{id}) |
order.created_at | timestamp | Placement date |
order.cancelled | boolean | Order was cancelled |
order.cancelled_at | timestamp | Cancellation date |
order.cancel_reason / order.cancel_reason_label | string | Cancellation reason code / display label |
order.financial_status / order.financial_status_label | string | Payment status code / display label |
order.fulfillment_status / order.fulfillment_status_label | string | Fulfillment status code / display label |
order.line_items | array | Line items |
order.item_count | number | Total quantity across line items |
order.subtotal_price | number | Subtotal after line discounts |
order.line_items_subtotal_price | number | Sum of line prices before order-level discounts |
order.total_price | number | Order total |
order.total_net_amount | number | Total minus refunds |
order.total_refunded_amount | number | Amount refunded |
order.tax_price | number | Total tax |
order.tax_lines | array | { title, rate, price } per tax |
order.shipping_price | number | Shipping total |
order.shipping_methods | array | { title, price } per shipping method |
order.shipping_address | object | Shipping address |
order.billing_address | object | Billing address |
order.discount_applications | array | All discount applications |
order.cart_level_discount_applications | array | Order-level discount applications |
order.total_discounts | number | Total discount amount |
order.transactions | array | { kind, status, gateway, amount, created_at } per transaction |
order.email / order.phone | string | Contact used at checkout |
order.note | string | Order note |
order.tags | array | Order tags |
order.metafields | object | Custom metafields |
All money values on order, its line items, and customer.total_spent
are integers in the currency’s minor units (e.g. cents). Always pipe
through the money filter family for display.
order line item
| Property | Type | Description |
|---|
line_item.title | string | Product + variant title |
line_item.quantity | number | Quantity ordered |
line_item.sku | string | SKU |
line_item.url | string | Product URL |
line_item.image | object | Line item image |
line_item.product / line_item.variant | object | Linked product / variant |
line_item.original_price / line_item.final_price | number | Unit price before / after discounts |
line_item.original_line_price / line_item.final_line_price | number | Line total before / after discounts |
line_item.unit_price | number | Unit-price measurement, where applicable |
line_item.properties | object | Custom line properties |
line_item.fulfillment | object | { tracking_company, tracking_number, tracking_url, created_at } |
line_item.successfully_fulfilled_quantity | number | Quantity fulfilled so far |
line_item.requires_shipping | boolean | false for digital products |
line_item.selling_plan_allocation | object | Present when bought on a selling plan — see platform extensions |
line_item.digital_downloads | array | Download links for digital products — see platform extensions |
address
Used by customer.addresses, customer.default_address,
order.shipping_address, and order.billing_address.
| Property | Type | Description |
|---|
address.id | string | Address ID |
address.first_name / address.last_name | string | Name parts |
address.name | string | Full name |
address.company | string | Company |
address.address1 / address.address2 | string | Street lines |
address.street | string | Combined street lines |
address.city | string | City |
address.province | string | Province / state name |
address.province_code | string | Province / state code |
address.country | object | Country — renders as the name; has name and iso_code |
address.country_code | string | ISO country code |
address.zip | string | Postal code |
address.phone | string | Phone |
address.url | string | Edit URL (/account/addresses/{id}) |
address.summary | string | One-line summary |
Render a full address block with the format_address filter:
The following properties are LaunchMyStore-specific — they go beyond
the classic customer-accounts object model, so guard them with {% if %}
in themes meant to stay portable.
line_item.selling_plan_allocation
Present on a line item when it was purchased on a
selling plan (subscription):
| Property | Description |
|---|
selling_plan_allocation.selling_plan.id | Selling plan ID |
selling_plan_allocation.selling_plan.name | Display name, e.g. Delivery every 1 Month |
selling_plan_allocation.selling_plan.recurring_deliveries | true for recurring plans |
selling_plan_allocation.selling_plan.options | Plan options ({ name, value }) |
order.subscription_status
On orders that carry an active subscription, the current billing status:
active, trialing, paused, past_due, or canceled.
line_item.requires_shipping and line_item.digital_downloads
Digital products have requires_shipping: false and, once the order is
paid, digital_downloads — an array of { name, url } download links the
customer can use from their order page.
Example: subscriptions + digital goods on customers/order
Helper JavaScript on account pages
Account pages automatically include a small platform helper script — themes
don’t need to load anything.
Country / province selector. Populate the country <select> with the
all_country_option_tags object. Each emitted <option> carries the
country name as its value and a data-provinces attribute holding that
country’s provinces as JSON. The helper wires a paired province <select>
to update whenever the country changes, and preselects the value named in
each select’s data-default attribute:
country_option_tags is also available and currently renders the same
full country list.
POST-link helper. The classic helper that submits a POST from a link —
used by address-book templates to delete an address by posting to
/account/addresses/{id} with _method=delete. Existing theme code that
calls the country/province and POST-link helpers works unmodified; the
plain-form alternative shown in the
customer_address section works everywhere.
Paginating orders
customer.orders works with the standard {% paginate %} tag:
SEO
Account pages are personal: the storefront emits
<meta name="robots" content="noindex"> on every customers/* page and
excludes /account routes from the sitemap. Themes don’t need to add
anything.