Key takeaways
- Dawn can show a size chart with no app. Its Pop-up block opens any store page in a dialog, so one page plus one block covers a store-wide chart.1
- Different charts per product need a metafield. Shopify’s own tutorial uses a product metafield of type Page, connected to the Pop-up block as a dynamic source.2
- Metaobjects pay off once you have many charts. They hold measurements as structured data you can render as a table, switch between inches and cm, and translate.
- Publish the chart as an HTML table, not only as an image. Screen readers and translation tools cannot read measurements inside a picture.
A Shopify size chart does not need a dedicated app: a page, a metafield and a block that ships with Dawn are enough for most apparel, footwear and jewellery stores. This guide shows how to add a size chart in Shopify three native ways, then covers what the basic tutorials skip: different charts for different products, accessible table markup, mobile layout and an inches/cm switch.
Whether you call it a size chart or a Shopify size guide, the setup is the same.
Why you can trust us
We have been in the Shopify space for over four years and have worked with hundreds of Shopify brands on their storefronts. Jacques has over 15 years of development experience. We build Fudge, an AI storefront editor with a 4.9 rating on the Shopify App Store and Built for Shopify status, and product page elements like a size guide are the kind of theme code it writes.
What are the ways to add a size chart in Shopify?
There are four native routes and one app route. Pick by how many different charts you need.
| Method | Per-product charts? | Structured data? | Effort | Best for |
|---|---|---|---|---|
| Page + Pop-up block | No, one chart per template | No, page content | Lowest, no code | Stores with one product category |
| Alternate product templates | One chart per template | No | Low, no code | 2-4 chart types |
| Page metafield + dynamic source | Yes | No, each chart is a page | Low, no code in Dawn | Several charts on a Dawn-family theme |
| Metaobject + Liquid | Yes | Yes | Medium, one Liquid snippet | Many charts, unit switching, translation |
| Size chart app | Yes | Varies by app | Low setup, depends on the app | Fit quizzes, vendor-supplied chart feeds |
The steps below use Dawn. Other themes name the block differently: Horizon’s Popup link block (blocks/popup-link.liquid) is filled with nested blocks instead of a page and can open as a drawer.
Method 1: One Shopify size chart for the whole store
- Go to Online Store → Pages → Add page. Title it “Size chart”, build the table with the Insert table button in the editor, set Visibility to Visible and save.
- Go to Online Store → Themes → Edit theme, then open your product template from the template dropdown.
- In Product information, click Add block and choose Pop-up.
- Set Link label to “Size chart”, pick your page in the Page setting, and save.
Those are the block’s only two settings.1 The link opens a dialog showing the page title and content. Drag the block directly under Variant picker so it sits with the size options.
Method 2: How do you show a different size chart per product?
Shopify’s documented answer is a product metafield of type Page, connected to the Pop-up block as a dynamic source.2 The block’s page setting accepts exactly that metafield type.3
- Go to Settings → Metafields and metaobjects → Products → Add definition. Name it “Size chart”, click Select type, choose Page, and save. Storefront access is on by default.
- Create one page per chart: tops, bottoms, shoes, ring sizes.
- In the theme editor, select the Pop-up block, click the dynamic source icon next to Page, and select your Size chart metafield.
- On each product, open Product metafields, click Size chart, and select the right page. A product can link to one page.
New to metafields? See how to add metafields to Shopify products.
The empty-link gotcha
Shopify’s tutorial puts the block on a separate product template, assigned only to products with a chart. The Dawn source shows why: the Pop-up button is rendered without checking whether a page is set, so a product with an empty metafield still gets the link, opening an empty dialog.1
To keep one template instead, duplicate your theme first, then wrap the existing opener markup in sections/main-product.liquid in a blank check:
{%- when 'popup' -%}
{%- if block.settings.page != blank -%}
<modal-opener ...>
...
</modal-opener>
{%- endif -%}
Alternate templates for 2-4 chart types
With only a few categories, skip the metafield. Create one product template per category, each with a Pop-up block pointing at a fixed page, then assign it under Theme template on the product or through the bulk editor’s Template column. A theme can hold 1,000 templates in total.4 See adding a custom template in Shopify for the steps.
Shopify’s category metafields are a different thing: they describe product attributes such as clothing size, not a table of measurements.
Method 3: Structured size charts with metaobjects
A page stores a chart as formatted text. A metaobject stores it as data with named fields, which makes unit switching, consistent markup and translation practical.
It is worth the extra setup when you have many charts or reuse them across markets. A definition can have up to 40 fields and 1,000,000 entries.5
Set up the definition and the reference
- Go to Content → Metaobjects → Add definition and name it “Size chart”.
- Add fields: Title (single line text), Measurements (JSON) and Fit notes (multi-line text). Leave Storefronts selected, and turn on Translations if you sell in more than one language.
- Add one entry per chart and keep it Active. A draft entry returns nothing in Liquid.5
- Go to Settings → Metafields and metaobjects → Products → Add definition, choose the Metaobject type, pick your Size chart definition, and save.
- On each product, select the entry in the Metafields section.
Store both units in the Measurements field:
{
"columns": ["Size", "Chest", "Waist"],
"rows": [
{"size": "S", "inches": ["34-36", "28-30"], "cm": ["86-91", "71-76"]},
{"size": "M", "inches": ["38-40", "32-34"], "cm": ["97-102", "81-86"]}
]
}
Render it as a table
Dawn has no block that turns a metaobject into a table, so this route needs Liquid. Duplicate your theme first, then add a Custom Liquid block under the Variant picker and paste:
{%- assign chart = product.metafields.custom.size_chart.value -%}
{%- if chart != blank -%}
{%- assign data = chart.measurements.value -%}
<details class="size-chart" data-unit="in">
<summary>Size guide</summary>
<div class="size-chart__scroll" role="region" aria-labelledby="SizeChartCaption" tabindex="0">
<table>
<caption id="SizeChartCaption">{{ chart.title.value | escape }}</caption>
<thead>
<tr>
{%- for column in data.columns -%}
<th scope="col">{{ column | escape }}</th>
{%- endfor -%}
</tr>
</thead>
<tbody>
{%- for row in data.rows -%}
<tr>
<th scope="row">{{ row.size | escape }}</th>
{%- for inches in row.inches -%}
<td>
<span class="u-in">{{ inches | escape }} in</span>
<span class="u-cm">{{ row.cm[forloop.index0] | escape }} cm</span>
</td>
{%- endfor -%}
</tr>
{%- endfor -%}
</tbody>
</table>
</div>
{%- if chart.fit_notes.value != blank -%}
<p>{{ chart.fit_notes.value | escape | newline_to_br }}</p>
{%- endif -%}
</details>
{%- endif -%}
Adjust custom.size_chart to your metafield’s namespace and key. The details element is a disclosure that works without JavaScript; a drawer or modal takes more code. The Custom Liquid setting accepts HTML and Liquid up to 50kb, so the CSS and script further down can live in the same block.
The Fudge route. Fudge reads the metaobject and writes the display layer. Tell Fudge:
“Add a ‘Size guide’ link next to the size selector on product pages that opens a drawer. Show the size chart from the product’s size chart metaobject as an accessible table, with an inches/cm toggle that remembers the choice.”
You get native theme code as a draft to review before publishing. The measurements still come from you and stay in metaobjects in the Shopify admin; AI and Shopify metafields explains that split.
How do you assign size charts at scale?
The bulk editor. Select products in the Products list, open the bulk editor, click Columns and add your metafield from the Metafields section. Shopify’s page does not list which metafield types the grid can edit, so try your size chart column on a few products first.6
CSV import. The product CSV accepts metafield columns for a fixed list of types. list.metaobject_reference is on it; the Page type and the single metaobject reference are not.6 To assign charts by spreadsheet, define the metafield as a list of values and read the first entry in Liquid.
By product type in Liquid. If your product types are consistent, skip per-product assignment. Name each entry so its handle matches a product type, then replace the first line of the Method 3 snippet with:
{%- assign chart = product.metafields.custom.size_chart.value -%}
{%- if chart == blank -%}
{%- assign type_handle = product.type | handleize -%}
{%- assign chart = metaobjects.size_chart[type_handle] -%}
{%- endif -%}
Here size_chart is the metaobject definition’s type. A product of type “Tops” picks up the entry with the handle tops, and a per-product metafield still wins. The same pattern works with pages through pages[handle], or with a tag check such as product.tags contains 'chart-tops'.
The Fudge route. Describe the rule and Fudge writes the Liquid for it. Tell Fudge:
“Use a different size chart for tops, bottoms and shoes based on product type, and hide the size guide link on products that have no chart.”
Fudge writes the rule into your theme as a draft. You still create the three charts in the admin.
What makes a Shopify size chart accessible?
Use a real HTML table. A screen reader or translation app cannot read numbers inside a picture, so an image-only chart fails WCAG 1.1.1 Non-text Content unless the same information is repeated as text.7
The relationships between sizes and measurements also have to be in the markup, which is WCAG 1.3.1 Info and Relationships.7 In practice:
- A
captionnaming the chart, such as “Women’s tops - body measurements”. - Column headers as
thcells withscope="col", and the size in each row as athwithscope="row". - A scroll container with
tabindex="0",role="region"and an accessible name, so keyboard users can scroll a wide table. - Text contrast of at least 4.5:1 (WCAG 1.4.3), including any pale zebra striping.
<div class="size-chart__scroll" role="region" aria-labelledby="chart-caption" tabindex="0">
<table>
<caption id="chart-caption">
Women's tops - body measurements
</caption>
<thead>
<tr>
<th scope="col">Size</th>
<th scope="col">Chest</th>
<th scope="col">Waist</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">S</th>
<td>34-36 in</td>
<td>28-30 in</td>
</tr>
</tbody>
</table>
</div>
Shopify’s help page for the editor’s table tool covers rows and columns, not captions or header cells. Click Show HTML in the page editor to check the markup and adjust it by hand.
The pop-up has rules too. A dialog should trap focus, close on Escape and return focus to the link that opened it. Dawn’s Pop-up block does all three: its source sets role="dialog" and aria-modal="true", loops Tab inside the dialog, closes on Escape or a backdrop click, and hands focus back to the opener.8 One wrinkle: it renders the page title as a second h1 on the product page.
Our WCAG 2.2 AA checklist for Shopify covers the rest of the product page.
How should a size chart work on mobile?
A six-column chart will not fit a phone screen, and that is acceptable: WCAG 1.4.10 Reflow exempts data tables, because rows and columns only make sense in two dimensions.
Scroll horizontally, with a cue. Dawn already does this inside its pop-up: below 750px its stylesheet sets tables to overflow-x: auto.1 Let the last visible column appear cut off, or add a “Scroll for more sizes” line, so shoppers know there is more.
Pin the first column. Once the size labels scroll away, the numbers lose their meaning. Sticky row headers fix that, and are easier to compare across than a stacked one-card-per-size layout:
.size-chart__scroll {
overflow-x: auto;
}
.size-chart__scroll th[scope='row'] {
position: sticky;
left: 0;
background: #fff;
}
Mind tap targets. WCAG 2.5.8 sets a 24 by 24 CSS pixel minimum. Dawn gives its pop-up link a min-height of 4.4rem; give the unit buttons similar room.
Drawer or modal? On phones a bottom drawer keeps the product visible behind it; a centred modal gives a wide table more room on desktop. Both need the same keyboard behaviour.
The Fudge route. Fudge makes the layout change in your theme’s CSS and markup. Tell Fudge:
“On mobile, make the size chart table scroll horizontally with the first column pinned.”
The change arrives as a draft you can preview on a phone before publishing.
How do you add an inches/cm toggle?
Store both units instead of converting on the fly. A converted value such as 87.63 cm has to be rounded, and rounded conversions will not always match your manufacturer’s metric spec. That is why the Measurements field above carries inches and cm.
The Liquid in Method 3 already prints both values in each cell. Add two buttons inside the size-chart element, above the table:
<div role="group" aria-label="Measurement units">
<button type="button" data-set-unit="in" aria-pressed="true">Inches</button>
<button type="button" data-set-unit="cm" aria-pressed="false">Centimetres</button>
</div>
.size-chart[data-unit='in'] .u-cm,
.size-chart[data-unit='cm'] .u-in {
display: none;
}
document.querySelectorAll('.size-chart').forEach(function (chart) {
var buttons = chart.querySelectorAll('[data-set-unit]')
function setUnit(unit) {
chart.dataset.unit = unit
buttons.forEach(function (button) {
button.setAttribute('aria-pressed', String(button.dataset.setUnit === unit))
})
try {
localStorage.setItem('sizeChartUnit', unit)
} catch (e) {}
}
var saved = null
try {
saved = localStorage.getItem('sizeChartUnit')
} catch (e) {}
if (saved === 'in' || saved === 'cm') setUnit(saved)
buttons.forEach(function (button) {
button.addEventListener('click', function () {
setUnit(button.dataset.setUnit)
})
})
})
aria-pressed tells assistive technology which unit is active, and the labels stay the same in both states, as the W3C toggle button pattern requires. The choice is saved in localStorage, so it carries to the next product.
International sizes are a related need. US, UK and EU sizes are labels, not conversions, so add them as extra columns from your manufacturer’s spec.
Where should the size guide link go, and what belongs beyond the table?
Put the link next to the size selector, because that is where the question comes up. For the wider layout, see how to customize a Shopify product page.
A table of numbers is only part of a useful guide. Add:
- How to measure. One line per measurement, saying where the tape goes.
- Body or garment measurements. Say which the chart shows.
- Model height and size worn, near the product images.
- Fit notes per product: runs small, relaxed fit, size up for layering.
Fashion stores will find more layout ideas in Shopify store design for fashion.
When is a size chart app the right call?
Native methods cover displaying a chart. An app earns its place when you need something beyond display:
- Fit recommendations. A quiz that asks for height, weight and fit preference and then suggests a size needs logic and data a theme snippet does not have.
- Very large catalogs with vendor-supplied charts. If hundreds of brands each send their own chart, a tool built to import and map them saves admin time.
Check the live App Store listing for current pricing. For display alone, a native table in your theme, written by hand or with the Shopify store editor, is enough.
FAQ
Dawn has no dedicated size chart setting, but the pieces are native. Its product template includes a Pop-up block that opens a store page in a dialog, and Shopify's own tutorial pairs it with a product metafield of type Page to show a chart per product. No app is required for either.
Create a product metafield of type Page under Settings, Metafields and metaobjects, then connect it to the Pop-up block as a dynamic source and select the right page on each product. For two to four chart types, separate product templates are simpler. For many charts, use a metaobject and assign it by product type in Liquid.
A table. Screen readers and translation apps cannot read measurements inside an image, and an image-only chart fails WCAG 1.1.1 Non-text Content unless the same information is repeated as text. Use an HTML table with a caption and header cells marked with scope.
In Dawn, the Pop-up block renders its link without checking whether a page is set, so an empty metafield still produces a link that opens an empty dialog. Either put the block on a separate product template, as Shopify's tutorial does, or wrap the block markup in a check that the page is not blank.
Store both sets of measurements instead of converting in the browser, because rounded conversions may not match the metric spec from your manufacturer. Print both values in each table cell, hide one with CSS, and switch with two buttons that use aria-pressed. Save the choice in localStorage so it carries across products.
Next to the size selector, as a link that opens a pop-up or drawer. In Dawn you can drag the Pop-up block directly under the Variant picker block in the theme editor. Include how-to-measure instructions and fit notes inside the chart, and show model height and size worn near the images.
Yes. The bulk editor can show metafield columns and a Template column, so you can set charts or templates for many products in one grid. The product CSV also accepts some metafield types, including lists of metaobject references. If your product types are consistent, a Liquid rule that maps product type to a chart avoids per-product assignment altogether.
Footnotes
-
Shopify, Dawn theme source,
sections/main-product.liquidandassets/section-main-product.css- thepopupblock has two settings, a Link label text field and apagepicker; the opener button is rendered with no check for an empty page; below 750px, tables inside the pop-up are set todisplay: block; overflow-x: auto; white-space: nowrap, and the pop-up link has amin-heightof 4.4rem. https://github.com/Shopify/dawn/blob/main/sections/main-product.liquid and https://github.com/Shopify/dawn/blob/main/assets/section-main-product.css ↩ ↩2 ↩3 ↩4 -
Shopify Help Center, “Adding a pop-up size chart to your product pages” - create a product metafield definition named Size chart with the Page type, add a Pop-up block to a product template, connect the block’s Page setting to the metafield with the dynamic source icon, and select a page on each product; “You can link only one page in the product metafield.” https://help.shopify.com/en/manual/custom-data/metafields/pop-up-tutorial ↩ ↩2
-
Shopify.dev, dynamic sources reference - the
pagesetting type accepts metafields of typepage_reference, and metaobject fields can be connected to compatible settings. https://shopify.dev/docs/storefronts/themes/architecture/settings/dynamic-sources ↩ -
Shopify Help Center, theme templates page - new templates are created from the template dropdown in the theme editor, assigned per product under Theme template or in the bulk editor’s Template column, and “you can have only 1,000 templates in total.” https://help.shopify.com/en/manual/online-store/themes/theme-structure/templates ↩
-
Shopify.dev, metaobject limits reference - each definition can have up to 40 fields and up to 1,000,000 entries, with 128 merchant-created definitions per store (256 on Plus). The Liquid
metaobjectreference adds that an entry with draft status returnsnil. https://shopify.dev/docs/apps/build/custom-data/metaobjects/metaobject-limits and https://shopify.dev/docs/api/liquid/objects/metaobject ↩ ↩2 -
Shopify Help Center, bulk editing metafields page - “You can edit metafields in bulk for products, variants, collections, and customers” by adding them under Columns. The product CSV page lists the metafield types supported for import and export, which include
list.metaobject_referencebut notpage_referenceor a singlemetaobject_reference. https://help.shopify.com/en/manual/custom-data/metafields/bulk-edit-metafields and https://help.shopify.com/en/manual/products/import-export/using-csv ↩ ↩2 -
W3C, Understanding WCAG 2.2 - SC 1.1.1 Non-text Content (Level A): “All non-text content that is presented to the user has a text alternative that serves the equivalent purpose”; SC 1.3.1 Info and Relationships (Level A), whose techniques include table markup, captions and the scope attribute. https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html and https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html ↩ ↩2
-
Shopify, Dawn theme source,
assets/global.js- theModalDialogclass closes on Escape and on a click on the backdrop, callstrapFocuswhen opened, and callsremoveTrapFocus(this.openedBy)on close, which returns focus to the button that opened it. https://github.com/Shopify/dawn/blob/main/assets/global.js ↩