How a Shopify page is put together
Knowing which file produced a line of HTML is most of the work. An Online Store 2.0 theme is organised like this:
| Where | What it produces | Can you edit it? |
layout/theme.liquid | The <html>, <head>
and <body> wrapper around every page | Yes, in the code editor |
templates/*.json | Which sections appear on each page type, and their order | Yes,
mostly through the theme editor |
sections/*.liquid | The markup for each section, such as header, product information,
featured collection | Yes |
snippets/*.liquid | Reusable pieces included with {% render %},
such as product cards, icons, price | Yes |
{{ content_for_header }} | Scripts and tags Shopify and some apps add to the head | No |
| App blocks and app embeds | Markup from apps built with theme app extensions | Settings
only; the code belongs to the app |
| Product, collection, page and blog content | HTML typed or pasted in the admin's rich text
editor | Yes, per item in the admin |
| Checkout and customer account pages | Rendered by Shopify | Not as theme HTML |
Most validator errors on a Shopify store sit in sections, snippets or content. Errors inside the scripts
output by content_for_header, or on checkout pages, are outside your control; note them
and move on.
โ ๏ธ Never edit the published theme directly. A single unclosed tag in theme.liquid
or the header section affects every page instantly. Always duplicate the theme first, then preview, then
publish.
Telling the sources apart in the page source
Open the page, view its source and use the line number the validator gives you. Shopify leaves useful clues:
- Section wrappers. Each section is output inside a wrapper with an id like
shopify-section-template--1234567890__main
and a class such as shopify-section. Scroll up from the error to the nearest wrapper and
its name tells you the section file; __main on a product page is usually main-product.liquid.
- App blocks are wrapped with a
shopify-block class and an id containing
the app's name or handle. An error inside one belongs to the app.
- App embeds usually appear just before the closing
</body> tag.
Turn them off one at a time under the theme editor's App embeds panel and re-validate to see which one
is responsible.
- Legacy app code is harder to spot: older apps edited theme files directly, adding
lines such as
{% render 'some-app-widget' %} or {% include %} to your templates.
Search the code editor for the app's name.
- Content appears inside the description or article body container, usually a
div
with a class such as rte. Errors there come from the text entered in the admin.
Validate the same template on two different products or articles. If an error appears on both at the
same place, it is theme or app code; if it only appears on one, it is that item's content.
Testing a store that is not public yet
If the store is behind a password page, an external checker sees only the password page. Either validate
after launch, share a theme preview link, or open the page in your browser, view the source, and paste
it into the W3C validator's direct input option. Check that you are testing the duplicated theme, not
the live one.
The Liquid mistakes behind most theme errors
Rich text settings inside a paragraph
A section setting of type richtext already outputs its own <p> or
list tags. A template that wraps it in another paragraph produces a paragraph inside a paragraph, which
the parser splits apart and the validator reports as a stray end tag:
{%- comment -%} Invalid: richtext already outputs <p> {%- endcomment -%}
<p class="banner__text">{{ section.settings.text }}</p>
{%- comment -%} Valid {%- endcomment -%}
<div class="banner__text">{{ section.settings.text }}</div>
For short text that should sit inside a heading or paragraph, use the inline_richtext
setting type instead, which allows bold, italic and links but no wrapping paragraph. The same trap applies
to {{ product.description }}, {{ article.content }} and rich text metafields:
always place them in a div, never a p or span.
Tags opened and closed under different conditions
{%- if card_product.url -%}<a href="{{ card_product.url }}">{%- endif -%}
{{ card_product.title | escape }}
{%- if show_link -%}</a>{%- endif -%}
If the two conditions ever differ, the page gets an unclosed or stray </a>. Use
the same condition for both halves, or better, output the whole element inside one if block
with an else for the unlinked version.
Unescaped values inside attributes
Product titles, alt text and metafield values can contain double quotes, such as a 27" monitor. Printed
raw inside alt="...", the quote ends the attribute early and the rest becomes junk attributes.
Use the escape filter on anything printed inside an attribute: alt="{{ product.title | escape }}".
Shopify's image_tag filter builds the image element for you, with width, height and alt
handled.
Hard-coded ids in sections that can be added twice
A section added twice to one template, or a product card snippet used in a loop, repeats any fixed
id. Build ids from section.id, block.id or the product's id. The duplicate
ids guide shows the pattern in Liquid, including the product form and quick-add modal case.
Blocks inside elements that cannot hold them
Theme blocks that output a div placed inside a <p>, <span>,
<button> or <a> wrapper create nesting errors. Check the wrapper
element in the section around {% for block in section.blocks %}, and use a div
when the blocks can contain block-level content.
App code: blocks, embeds and what uninstalling leaves behind
Apps reach your storefront in three ways, and each is handled differently.
- App blocks are added to sections in the theme editor. If one produces invalid markup,
check its settings, update the app, and report the error to the developer. You can remove the block from
a template without uninstalling the app.
- App embeds are switched on and off in the theme editor's App embeds panel. They
are removed cleanly when disabled, which makes them easy to test: switch one off, preview, validate.
- Direct theme edits by older apps, and by developers following an app's manual install
steps, add snippets and
render or include tags to your theme files. Uninstalling
the app does not remove them. The snippet may keep printing an empty container or a broken tag, or the
render tag may point at a snippet that no longer exists, which Liquid reports as an error
message in the output.
To clean up leftovers, list the apps you have removed, search the code editor for each name, and check
the snippets folder for files named after them. Remove the render line and
then the snippet file, on the duplicated theme, and preview the affected templates before publishing.
๐ก Before removing a leftover snippet, search the whole theme for its file name. One
snippet can be rendered from several sections, and deleting the file while a reference remains replaces
a validation error with a visible Liquid error on the page.
Scripts that apps inject through content_for_header cannot be edited at all. If one outputs
invalid markup, report it to the developer and weigh the feature against the problem.
Errors inside product descriptions and pages
Content errors usually come from copying descriptions out of a supplier's website, a spreadsheet,
a word processor or another platform during migration. Typical finds are <font> and
<center> tags, empty paragraphs, tables with fixed widths, unclosed list items and
whole <html> or <body> fragments pasted from a full page.
- Use the editor's HTML view. The rich text editor in the Shopify admin has a button
to show HTML. Open it on the affected item and delete the offending tags directly.
- Paste as plain text. When adding new descriptions from other sources, paste without
formatting and reapply bold, lists and links using the editor's own buttons.
- Fix in bulk with a CSV. For hundreds of products, export products to CSV, clean
the
Body (HTML) column with a careful search and replace in a text editor, and re-import
a test batch before the rest. Keep the original export as a backup.
- Metafields. Rich text metafields store structured content and are rendered by Shopify,
so they rarely contain invalid HTML; errors around them usually come from the template wrapping them
in the wrong element, as described above.
For obsolete tags in particular, the deprecated
HTML guide lists the modern replacement for each one.
The fix, step by step
Step 1
Duplicate the live theme and work
on the copy
In the Shopify admin, go to Online Store, then Themes, open the menu beside the current
theme and choose Duplicate. Rename the copy with the date so it is easy to identify. Make every code
change on this copy, and use its preview to test. If you develop locally, pull the copy with Shopify
CLI or connect it to GitHub, so every edit is tracked and can be reverted in seconds if something goes
wrong.
Step 2
Validate one URL per template type
Run
the HTML Checker on the home page, a collection, a product, a blog article, a standard page, the search
results and the cart. Add any alternate templates you use, such as a second product template for bundles.
Record the errors per template. Shopify reuses the header, footer and product card across most pages,
so errors that appear on every URL will usually trace back to one section or snippet file.
Step 3
Assign each error to a source
Use
the line number to find each error in the page source, then look upwards for the nearest section wrapper,
app block wrapper or content container. Label each error as theme, app block, app embed, leftover app
code, Shopify output or content. Compare two products or articles on the same template to separate content
errors from template errors. Errors inside Shopify's own injected scripts or checkout go on a known-issues
list rather than the fix list.
Step 4
Fix Liquid errors in sections and
snippets
Open the section or snippet named by the wrapper in the code editor of your duplicated
theme. Look for rich text settings or descriptions wrapped in a paragraph, links or tags opened and closed
under different conditions, values printed in attributes without the escape filter, hard-coded
ids and block loops inside inline wrappers. Fix the first error in each file, preview the page, and validate
again, because one broken tag often causes several later messages.
Step 5
Clean up app blocks, app embeds
and leftover app code
For errors inside app markup, update the app and review its settings first.
Switch app embeds off one at a time in the duplicated theme to confirm which one is responsible. Then
search the code editor for the names of apps you have uninstalled, remove their render or
include lines, and delete their snippet files once nothing references them. Report remaining
errors in active apps to their developers with the URL.
Step 6
Clean product descriptions and
page content
For errors that appear on one item only, open that product, page or article in the
admin and switch the rich text editor to its HTML view. Remove obsolete tags, pasted page fragments,
empty paragraphs and unclosed list items. Where the same problem affects many products, export the catalogue
to CSV, clean the body HTML column, and re-import a small test batch first, keeping the original file
as a backup.
Step 7
Run theme check, re-validate and
publish
If you use Shopify CLI, run shopify theme check on the duplicated theme to
catch Liquid problems such as missing snippets and deprecated tags that a page validator cannot see.
Then validate the preview URLs of every template again, click through key journeys including add to basket
and search, and check the mobile menu and any quick-view modals. When everything passes, publish the
duplicated theme and keep the old one unpublished for a while as a fallback.
Theme updates, customisations and keeping it clean
Code edits and theme updates pull in opposite directions. When your theme developer releases a new
version, your changes to its files are not carried across automatically, so fixes made in theme files
may need reapplying after an update. A few habits keep that manageable:
- Keep a change log. Record each file you edited and why, or keep the theme in GitHub
using Shopify's GitHub integration so every change is a commit you can compare and reapply.
- Prefer settings over code. Use section and block settings, custom CSS fields in
the theme editor and app blocks rather than editing theme files, since settings generally survive updates
better than code edits.
- Report bugs upstream. If an error is in the theme's own code, tell the theme developer.
Fixed at the source, it stays fixed for every future version.
- Be careful with Custom Liquid sections. A Custom Liquid section lets anyone with
theme access paste raw code into a template. It is useful, but it is also the easiest way to add unclosed
tags. Validate the page every time one is added or changed.
- Re-validate after installing any app. New apps are the most common cause of new
errors on an otherwise clean theme.
Accessibility checks on Shopify, such as colour contrast and keyboard access, are covered separately
in the Shopify accessibility guide.
Questions and answers
1. Does invalid HTML hurt my Shopify store's rankings?
Not in itself. Google has said it does not use HTML validation as a ranking factor,
and most Shopify stores have some validation errors. What matters is whether an error changes how the
page works or is understood: an unclosed tag in the header that pushes content into the wrong container,
broken markup around product information, or a form that does not submit properly. Fix those first. Treat
the rest as maintenance that improves accessibility and makes the theme easier to work on.
2. Why do I see errors from apps I uninstalled months ago?
Because some apps, particularly older ones, edited your theme files directly when they
were installed, adding snippet files and render or include tags to your templates. Uninstalling the app
removes its access and its admin screens but does not undo those edits. The leftover code can keep printing
empty containers, broken tags or error messages. Search the theme's code editor for the app's name, remove
the references on a duplicated theme, and then delete the unused snippet files.
3. Can I fix errors on the Shopify checkout page?
Not as theme HTML. Checkout pages are rendered by Shopify, and merchants customise
them through the checkout editor and checkout extensions rather than by editing template markup. Errors
the validator finds there are Shopify's to fix. Record them as known issues so they are not investigated
again at the next audit, and concentrate on the pages you control: the home page, collections, products,
the cart, search and your content pages, where visitors spend most of their time.
4. Will a Shopify theme update undo my fixes?
It can. When you update a theme to a new version, your code edits to the old version's
files are not merged in automatically, so changes to sections and snippets may need to be reapplied.
Keeping a change log, or tracking the theme in GitHub, turns that into a quick comparison rather than
detective work. Fixes you report to the theme developer and they release in an update are the most durable,
because they stay fixed for every future version.
5. Why does wrapping the product description in a p tag cause an error?
The product description is stored as HTML and usually already contains its own paragraphs,
lists or headings. A paragraph cannot contain another paragraph or a block element such as a list, so
when the browser meets the inner opening tag it closes the outer paragraph early, and the closing tag
at the end is left with nothing to close. The validator reports that as a stray end tag. Wrap descriptions,
article content and rich text settings in a div instead.
6. Is it safe to edit theme code if I am not a developer?
Small, careful edits are manageable if you always work on a duplicated theme, change
one thing at a time and preview before publishing. The risk comes from editing the live theme or making
many changes at once. If an edit goes wrong, the code editor keeps older versions of each file that you
can restore, and the unpublished original theme remains as a fallback. For larger fixes, such as rewriting
a section, a Shopify developer will usually be quicker and safer.
Validate your Shopify templates
The HTML Checker validates each page against W3C standards and reports errors and warnings covering
the doctype, encoding, nesting, deprecated elements, duplicate IDs, ARIA, forms and the language attribute,
so you can fix each template at its source.
Open HTML Checker Fixes โ