Skip to content

Asset Organization & Performance

TL;DR: The rules shared across every media type — reserving space, hosting on Shopify’s CDN, naming and pruning assets, and auditing for bloat over time.

Icon Management, Responsive Images, Video Management, and 3D & AR Media each cover one media type in depth. This page covers what’s shared across all of them: the one performance rule that applies no matter the media type, how Shopify’s CDN hosting actually works, and the organizational habits that keep a theme’s asset footprint maintainable as it grows past its first few sections.

The one rule that applies to every media type: reserve its space

Section titled “The one rule that applies to every media type: reserve its space”

Every media element needs its space reserved on the page before it actually loads. Do this with explicit width/height attributes (which image_tag, video_tag, and model_viewer_tag all provide by default) or an explicit aspect-ratio in your CSS. Skipping this step is the most common cause of layout shift, and both Lighthouse and Core Web Vitals penalize your score for it — no matter whether the media is an image, a video, or a 3D viewer.

/* A reusable pattern for any media type */
.media-wrapper {
aspect-ratio: var(--media-aspect-ratio, 16 / 9);
overflow: hidden;
}
.media-wrapper > * {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="media-wrapper" style="--media-aspect-ratio: {{ media.aspect_ratio }};">
{{ media | image_url: width: 800 | image_tag }}
</div>
Media typeLiquid filter(s)Biggest performance leverFull guide
IconInline SVG snippetcurrentColor + em sizing, no extra asset request at allIcon Management
Imageimage_url + image_tagRequest close to actual rendered size; automatic lazy-loading below the foldResponsive Images
Videovideo_tag / external_video_tagAlways a sized poster; adaptive HLS is automatic for Shopify-hosted videoVideo Management
3D modelmodel_viewer_tagreveal: 'interaction' by default, not auto3D & AR Media

Host assets on Shopify’s own CDN, not a third party

Section titled “Host assets on Shopify’s own CDN, not a third party”

Deliver as much as you can from the Shopify CDN rather than an external host. Using the same host for your theme’s static assets avoids extra HTTP connections to a different domain and lets the browser prioritize resource delivery correctly. In a Shopify theme, this means putting static files (icons you ship as raster images, logos, background textures) in the theme’s assets/ folder, not linking out to an external image host or third-party CDN.

{% comment %} A static, theme-shipped image (not a product/media image) {% endcomment %}
{{ 'brand-logo.svg' | asset_url | image_tag: alt: shop.name }}
{% comment %} asset_img_url is a shortcut for asset-folder IMAGES
specifically, returning a pre-sized CDN version (default 'small',
100x100) — useful for a static thumbnail you don't need full
image_url/image_tag control over {% endcomment %}
{{ 'icon-placeholder.png' | asset_img_url: 'medium' }}

Product, variant, and collection images already live on Shopify’s CDN automatically since they’re uploaded through the Shopify admin — this rule mainly matters for the static files you commit directly into your theme’s assets/ folder.

Preloading media deliberately, not by default

Section titled “Preloading media deliberately, not by default”

You can add up to two resource hints per template, using the preload_tag filter or the preload keyword on image_tag. Reserve this for the one or two resources that are genuinely critical to the first paint, almost always your LCP candidate:

{{ section.settings.hero_image | image_url: width: 1600 | image_tag: preload: true, loading: 'eager', fetchpriority: 'high' }}

See Performance Strategy for the fuller preload/prefetch/preconnect discussion — the same “use sparingly” rule that applies to CSS and JS resource hints applies here too.

Naming and organizing assets so they scale

Section titled “Naming and organizing assets so they scale”

A theme’s assets/ folder and its icon snippets/ grow continuously over a project’s life. A few habits keep that growth navigable instead of turning into an unsorted pile:

Asset typeNaming conventionWhere it lives
Icon snippetsicon-*.liquid (kebab-case)snippets/
Static images (logos, textures, placeholders)Descriptive kebab-case, e.g. brand-logo.svg, empty-cart-illustration.svgassets/
Product/variant/collection mediaManaged by Shopify Admin, not committed to the themeShopify’s own CDN

See Snippets & Naming Conventions for the theme-wide naming rules this table follows.

A theme’s media footprint tends to grow in one direction, additively, unless someone deliberately prunes it. A few checks worth doing periodically, not just once at launch:

  • Grep for render 'icon- across the codebase before deleting a section or feature, and remove any icon snippet nothing renders anymore in the same PR. See Icon Management: keeping the icon set maintainable for the full habit.
  • Check assets/ for orphaned static files — an old hero background image or a logo variant from a since-reverted design change, still sitting in the folder and still shipping in every theme package even though nothing references it anymore.
  • Re-check reveal: 'interaction' and lazy-loading defaults after a redesign. A section that moves from below the fold to above it (or vice versa) needs its loading behavior re-verified, not left on whatever default it happened to load with originally.
  • Run Lighthouse after adding any new media-heavy section, not just at the pre-submission audit. A single unsized image or a missed reveal: 'interaction' default is a much smaller fix caught immediately than it is three sections later. See Performance Strategy: phase by phase for the full milestone-audit habit this applies to media specifically.
✅ Do❌ Don’t
Reserve space for every media element, using explicit dimensions or aspect-ratio, before it loads — no matter the media type.Linking to an externally-hosted image, font, or texture instead of committing it to assets/ and serving it from Shopify’s own CDN.
Host static theme assets on Shopify’s own CDN via assets/, not an external host.Preloading more than one or two resources per template, which competes with the resource that actually matters for bandwidth.
Preload at most one or two resources per template, and only your genuine LCP candidate.Letting unused icon snippets and orphaned static assets accumulate without ever being pruned.
Keep asset naming consistent (icon-* for icon snippets, descriptive kebab-case for static images) so the codebase stays navigable as it grows.Never re-checking a media element’s loading defaults after a redesign moves it above or below the fold.
Audit for unused icons, orphaned static assets, and stale loading defaults periodically, not only once at project launch.Treating a media-heavy section’s performance impact as something to check only at the pre-submission Lighthouse run, instead of at the milestone it was actually added.
  • Reserve space for every media type before it loads: explicit dimensions or aspect-ratio.
  • Host static assets (assets/ folder) on Shopify’s own CDN; product/variant/collection media is already CDN-hosted via the Admin.
  • preload_tag / image_tag’s preload param: at most one or two per template, reserved for the real LCP candidate.
  • Consistent naming (icon-*.liquid, descriptive kebab-case for static files) keeps a growing asset footprint navigable.
  • Audit for unused icons, orphaned assets, and stale loading defaults on a regular cadence, not just at launch.