Shopify theme customization vs apps: which changes belong where
When a change belongs in a theme section or block and when it needs an app, what each route puts on the page, and a ten-minute check of your product page.
8 min read
Contents · 6 sections
Where does the code for a change actually live?
A Shopify storefront gets its code from three places, and the choice between customising the theme and installing an app is a choice between them. Shopify's theme architecture describes the first: sections are Liquid files in the sections directory, rendered from JSON templates, and merchants configure them in the theme editor. A section can bundle its own JavaScript and CSS with the {% javascript %} and {% stylesheet %} tags, and Shopify's asset guidance says that bundled assets are injected once per file, not once per instance, and that the stylesheet is subset so each page loads only the styles of the files in its render tree.
The second is the app block. Shopify's app blocks page explains that a section opts in with a block of type @app, after which a merchant can place app content inside it without anyone editing theme code. Statically rendered sections cannot take app blocks.
The third is the app embed block, which has no place in the layout. The theme app extensions reference says Shopify injects it before the closing head or body tag, that it is deactivated after install until the merchant switches it on under Theme settings, App embeds, and that a JavaScript file named in its schema is added as a script async tag in the head. It is meant for floating or overlaid elements and for apps with no interface of their own.
So the first question for any change is not which is better. It is which of these three routes would carry it, and what each route puts on the page.
When does a section or theme block do the job better?
A change belongs in the theme when Shopify can already render everything it needs. That covers most of what stores ask for:
- Layout and ordering: an extra row of icons, a trust strip under the add-to-cart button, a different arrangement of the gallery.
- Copy and imagery that the merchant edits: announcement text, shipping notes, size guidance, FAQ content held in section blocks.
- Content driven by data the store already has: product and collection fields, metafields, metaobjects, translations.
- Styling and interaction that do not need a server: an accordion, a tab set, a sticky bar.
The cost of these is the cost of Liquid and CSS that ship with the theme. Nothing is fetched from a third-party origin. Theme blocks make this easier than it used to be: a block lives in the /blocks folder, is reusable across sections, can nest other blocks, and can be added, removed and reordered by the merchant. A theme can hold at most 300 of them, counting every .liquid file in the folder, referenced or not. Section blocks are the lighter form, limited to the section that defines them, a single level deep, and not usable in the same section as theme blocks.
Two cautions apply. A section statically rendered with the section tag cannot be removed or reordered by merchants, and Shopify's own page advises avoiding it where a JSON template will do. And edits to the theme make later upgrades of the theme harder: every customisation is a difference to carry forward. That is a real cost, and it is paid by whoever maintains the store, not by the visitor.
When is an app the right route?
An app earns its place when the job needs something the theme cannot hold. Reviews that are collected, moderated and syndicated, subscriptions with their own billing logic, search with an index behind it, a returns portal, loyalty balances: each of these has data and behaviour that live outside Liquid. Writing them into the theme means building and maintaining a system, not a section.
The route the app uses matters as much as the decision to install one. The theme app extension framework is built so that the merchant does not edit theme code, which lowers the risk of breaking the theme, and Shopify says plainly that an app block lets the merchant place its content precisely inside a section. The same reference makes a point that bears directly on speed: an app embed block can load its scripts only on specific pages, which the older script tag cannot do, and doing so keeps the app's impact to the pages that need it. All assets in an extension are served from Shopify's CDN.
Read that as a question to put to each vendor. Does the app render through an app block, so that it is on the product page only where you placed it? Does it use an embed, and if so, is it restricted to the templates that need it with enabled_on? Or does it still load one file on every page? The answer is usually visible in the page source and the Network panel before any conversation with the vendor.
How do you tell whether an app is doing a theme's work?
Take the page that sells, the best-selling product page, and run this check, which takes about ten minutes.
- In the Shopify admin open Online Store, Themes, Customize, and note the apps present in the product template: app blocks appear inside sections, and App embeds appear under Theme settings.
- List what each one shows. If the visible output is static, such as a badge strip, a shipping note, an accordion or a block of fixed text, a section could render it.
- Open DevTools, Network, filter by JavaScript, reload, and note which files come from the app. A widget that only displays text but loads a script has a theme equivalent.
- Check whether the app is switched on for templates where it shows nothing. An embed that is active on collection and cart pages for a feature that exists only on product pages is a scoping fix.
The result is a short list in three columns: needs an app, could be a section, could be limited to fewer templates. Our third-party scripts guide shows how to price each script in milliseconds, and the page weight guide covers the byte budget the list feeds into.
Shopify's own guidance on removing render-blocking apps gives the order in which to act. High priority, remove immediately: unused or rarely used apps, apps with duplicate functionality, apps with a native Shopify alternative, and apps causing 500 ms or more of blocking time. Medium priority, optimise or replace: apps used occasionally, apps with async loading options, apps that can be loaded conditionally. Low priority, optimise only: business-critical apps, apps with no alternative, and apps with under 100 ms of blocking time. A theme section is the usual native alternative in the first tier.
What changes when you move an app's job into the theme?
Three things change, and one stays the same.
The script goes. The widget's JavaScript, its stylesheet and its third-party requests leave the page, and so does whatever main-thread time they took. That is the saving to measure, and the way to measure it is to record the product page before and after on the same throttling profile.
The editing model changes. A section is edited in the theme editor with settings you define, so the person who changes the wording needs no app dashboard. The price is that you now own the markup and have to keep it accessible, responsive and consistent with theme upgrades.
The data model needs a home. A fixed badge strip needs none, but something like per-product messaging needs a metafield or metaobject definition so that content stays editable per product instead of being hard-coded in Liquid. Define the field first, then build the section that reads it.
What stays the same is the need to test. Check the product page on a 390 pixel viewport, check the editor preview, and search the theme for any snippet or render call the old app left behind.
What should you fix first, and how long does it take?
Start with the apps whose output is static. Move each into a section or theme block, delete the app, and remove any leftover snippet. A badge strip or accordion is a half-day of work each, including testing on a phone; a multi-field section driven by metafields is a day.
Second, scope what stays. For every app embed, ask the vendor whether the script can be limited to the templates that use it, and use enabled_on or disabled_on where the extension allows it.
Third, keep the apps that hold data and logic, and keep them to one per job. If the list of apps on your product page is longer than you can explain in a sentence each, our Store Teardown measures what they cost on your own store, and we can say which belong in the theme.
The inventory takes ten minutes, the first two moves an afternoon, and a re-measurement the next morning.