Shopify themes
Next release
How midcode runs a Shopify theme with your own Shopify CLI, what you can edit in its Liquid and JSON, and how the theme reaches your store.
This ships with the next release of midcode. The version you can download today (1.1.2) doesn’t have it yet.
midcode opens a Shopify theme’s folder and shows your storefront at every breakpoint, drawn by Shopify with your store’s own products. Click an element and midcode knows the .liquid file and line it’s written in. Text, classes, attributes and layout are written into the theme’s files: the Liquid markup, or the JSON template or locale file where a text really lives.
Everything goes through your own Shopify CLI, signed in as you. midcode stores no Shopify token. It works on a development theme, so the theme your visitors see doesn’t change until you send or publish it yourself.
Two more pages cover the rest: Sections and blocks for what Shopify’s theme editor has in its sidebar, and Store for products, orders and the store’s other data.
At a glance
| Shopify themes | |
|---|---|
| Detected by | A layout/theme.liquid file in the folder |
| Runs with | Your Shopify CLI: shopify theme dev, on a copy of the theme |
| Elements are marked by | midcode, in that copy. Your own files are never marked |
| Editing | Text, classes and attributes; insert, move and delete elements in .liquid files; text kept in templates/*.json and locales/*.json; section settings |
| Components | Not recognized. A snippet is edited where it’s written |
| Pages | The theme’s templates, at the addresses your store gives them |
| Styles | mid: utilities on the element, their CSS in assets/midcode.css |
| Tried with | Dawn, end to end, on a development store |
Open a theme
Press ⌘O and choose the theme’s folder (see Open a project). You need a Shopify account that can sign in to the store: a Partner account, or a staff account of the store.
The first time, the canvas shows a card for each thing the Shopify CLI would have asked in a terminal, in this order. A card only shows when its answer is missing.
| Card | What runs | Where the answer is kept |
|---|---|---|
| Install the Shopify CLI | npm install -g @shopify/cli@latest | On your Mac, as a global npm package |
| Sign in to Shopify | shopify auth login | In the Shopify CLI’s own session |
| Which store? | shopify store list | .midcode/shopify.json, in the theme |
| The storefront’s password | Nothing: it’s handed to shopify theme dev | Encrypted on your Mac, outside the project |
Install the Shopify CLI
Shows when shopify isn’t on your shell’s path. “Install it” runs npm in your own login shell and shows what it prints. You can also run npm install -g @shopify/cli in a terminal yourself. Without npm on the Mac, midcode says so: install Node.js, or the Shopify CLI with Homebrew, and try again.
Sign in to Shopify
“Sign in” runs shopify auth login. Your browser opens Shopify’s sign-in, and the card shows a verification code to check against the one in the browser. midcode waits up to 10 minutes; “Cancel” stops it. The session belongs to the CLI: if you had already signed in from a terminal, this card doesn’t show.
Which store?
The card lists the stores of your account, with development stores marked “Development”. Pick one, or type another in the field (your-store.myshopify.com, the store’s name alone, or its admin address) and click “Use it”. midcode writes it into the theme’s folder:
{
"store": "your-store.myshopify.com"
}That file holds the store’s address and nothing secret (it’s listed with everything else in What midcode adds to your project). When it isn’t there, midcode reads the store from what the theme already says and doesn’t ask: a --store in the command of .midcode/server.json, or a store = "…" line in shopify.theme.toml.
The storefront’s password
Every development store is behind a password, and so is any store with password protection on. shopify theme dev stops when it meets one, and the canvas asks for it. It’s in the store’s admin, under Online Store → Preferences. Type it and click “Continue”.
midcode keeps the password encrypted on your Mac (~/Library/Application Support/midcode/shopify.bin), never in the project, and hands it to the CLI each time it starts the theme. If Shopify refuses it, the card says “Shopify didn’t take that password. Check it and try again.”
A fifth card, “Connect the store”, is only for the store’s data: see Store.
How midcode runs it
Liquid is rendered by Shopify from the files the CLI uploads, so nothing of midcode’s can run there. The marks get in another way.
First, midcode copies the theme into its own data folder (~/Library/Application Support/midcode/shopify/): the folders assets, blocks, config, layout, locales, sections, snippets and templates, plus .shopifyignore and shopify.theme.toml. In the copy, each element of each .liquid file says where it’s written:
<h2 data-mc="sections/hero.liquid:42:5" class="banner__heading">{{ section.settings.heading }}</h2>Then it runs your CLI on that copy:
shopify theme dev --path <the copy> --host 127.0.0.1 --port <a free port> --store your-store.myshopify.comThe CLI uploads the copy to a development theme in your store and serves its preview. That preview is what the canvas shows.
An edit is written to your real file. A moment later the copy follows (marked again), the CLI uploads the change and the preview reloads. With Dawn, an edit showed in about 2 seconds.
Your folder never carries a mark, so git diff shows your edit and nothing else. The copy is made new each time the theme starts. The live theme is not touched by any of this.
“Change how it runs”, under each card, lets you set the command yourself (see Any other stack). Keep --path $THEME in it: $THEME is the copy. With your own folder as the path the storefront still shows, but its elements carry no marks and there’s nothing to edit by.
What you can edit
Liquid
In a .liquid file midcode edits what’s written as markup and leaves alone what Liquid computes.
Elements: select, restyle, change attributes, delete, insert, and move within the same file. Several can be deleted, moved or wrapped in a stack (⇧A) at once, and ⌥-drag leaves a copy in the same file. A tag with a hyphen (
<product-info>,<slider-component>) is a custom element, and an element like any other.Text written between tags is edited with a double-click (see Text, images and video).
Expressions:
{{ … }}and{% … %}are shown, not edited. A heading that is{{ section.settings.heading }}has no text in that file (see below).Classes: the ones written in
class="…"are changed. What Liquid adds to the attribute stays as it is.Whole blocks:
{% schema %},{% javascript %},{% stylesheet %},{% style %},{% raw %}and{% doc %}are left as they are, and{% comment %}is a comment. The settings a schema declares are edited in the Section panel.Snippets: an element inside
snippets/card-product.liquidis written once. Changing it changes every place the snippet is rendered.
An element opened in one branch and closed in another can’t be moved or removed, because midcode can’t tell where it ends:
{% if block.settings.link != blank %}
<a href="{{ block.settings.link }}" class="card">
{% else %}
<div class="card">
{% endif %}midcode answers “An expression draws this element ({…}). Move or remove it in the code.” Its classes and attributes can still be changed.
Insert adds plain markup (frames, stacks, text, sections). midcode’s interactive components are React, and a theme refuses them: “This component needs a React page”.
Text that isn’t in the .liquid file
A theme’s texts are rarely in its Liquid. Double-click a heading: midcode tries the .liquid file first, and when the text is printed by an expression it looks for that exact text in the theme’s JSON and changes it there. A section’s own text is in the template:
"settings": {- "heading": "Summer sale",+ "heading": "Summer collection",A text of the theme itself is in a locale file:
"cart": {- "title": "Your cart",+ "title": "Your bag",The text has to be a whole value in the JSON. A rich text setting keeps its tags (
"<p>…</p>"), so it isn’t found this way: change it in the Section panel.If the same text is written in several places, midcode names them and doesn’t guess: “That text appears in 3 places (…). Open the right one in your editor.” Short words that many locale files share end up here.
Text handed to a snippet (
{% render 'button', label: 'Shop now' %}) is Liquid code: change it in the code.
Each edit is one ⌘Z step and shows in Publish.
Pages
The page menu in the top bar lists the templates the theme has, at the store’s addresses. A template counts when templates/<name>.json or templates/<name>.liquid exists.
| Template | Address |
|---|---|
index | / |
list-collections | /collections |
collection | /collections/all and /collections/[handle] |
product | /products/[handle] |
page | /pages/[handle] |
blog | /blogs/[blog] |
article | /blogs/[blog]/[handle] |
cart | /cart |
search | /search |
An address with brackets is one template drawn for many things. midcode lists the real ones from your storefront (its sitemap and the links on its pages, up to 200 per address), so you design the product template looking at an actual product. See Pages and navigation.
Styles
A theme has no Tailwind, so the style panel writes midcode’s own utilities, mid: (see Without Tailwind). The first style edit creates assets/midcode.css and adds its tag at the end of the <head> of layout/theme.liquid:
{{ content_for_header }}+ {{ 'midcode.css' | asset_url | stylesheet_tag }} </head>From then on a style is a class on the element, and its CSS is kept in that file:
<h2 class="banner__heading mid:text-[56px] mid:md:text-[80px]">{{ section.settings.heading }}</h2>The theme’s own CSS and classes are not touched. assets/midcode.css is part of the theme and goes to the store with it.
Languages
The theme’s locales/*.json are its languages. The file named en.default.json marks the default, and the *.schema.json files (the theme editor’s own labels) are left out. With two or more languages, Translations lists every text per language and writes each value into those files: see Languages. In a theme the language menu doesn’t switch the canvas to another language.
Create a theme
“New project” in the hub has Shopify as a technology (see New project). It needs the CLI and a sign-in, and asks with the same two cards.
| Starter | What runs |
|---|---|
| Dawn | shopify theme init <name> --path <folder> --clone-url https://github.com/Shopify/dawn.git |
| Skeleton | shopify theme init <name> --path <folder> |
| A theme from your store | shopify theme pull --store <store> --theme <id> --path <folder> |
For the first two, choose the “Store to work on it with”, or “Decide later”. For a theme from your store, choose the store and the theme, then “Download and open”.
Send the theme to your store
Two things are separate here.
Publish commits and pushes the theme’s code with git (see also GitHub). It doesn’t send anything to Shopify. If the theme in your store is connected to that GitHub branch, Shopify updates it from the push.
Store → Themes sends the folder itself, with shopify theme push from your real folder, never from the marked copy. In a theme, the Publish panel has “Send the theme to your store” at the top, which opens it.
| Button | What it does |
|---|---|
| Send as a new theme | Uploads the folder as a new, unpublished theme named <project> (midcode) |
| Send here | Replaces an existing theme’s files with the folder’s. Files that aren’t in the folder are removed from it |
| Publish | Makes an unpublished theme the store’s theme (shopify theme publish) |
“Send here” and “Publish” ask first. On the live theme the question is “Send this theme to the live store?”, and it says what that means: it replaces what every visitor sees, right away. Each theme also has “Preview” and “Customize”, which opens it in Shopify’s editor. If Shopify takes the theme but finds something wrong in some files, midcode lists them.
Limits
Tried end to end with Dawn on a development store: 488 elements marked on the home page. Other themes were not tried.
What Liquid prints (
{{ product.title }}) is data from your store. It’s changed in Store, not on the canvas.Templates outside the table above (customer accounts,
404,password,gift_card, alternates such asproduct.featured.json) are not in the page menu.Only
layout/theme.liquidgets the stylesheet’s tag. Another layout, such aslayout/password.liquid, doesn’t loadassets/midcode.css.A theme folder whose
package.jsondepends on Vite, Next.js, Astro, SvelteKit, Nuxt, React Router or Remix is opened as that kind of project, not as a theme.Component mode and creating pages from midcode are not available in a theme. A page of a store is made in Shopify’s admin, or in Store.
Site settings wasn’t made for themes and wasn’t tried in one. The globe shows, because a theme’s layouts close a
<head>, but the form reads the first of them by name (layout/password.liquidbeforelayout/theme.liquid), and a theme prints its title and description with Liquid, which the form shows as code and doesn’t write. Set a store’s title, description and social image in Shopify’s admin.Two midcode tabs on the same store share one development theme: it belongs to the CLI.
Troubleshooting
The canvas keeps asking for the password. Shopify refused it. Copy it again from Online Store → Preferences. A wrong password also makes the CLI forget one it had remembered from a terminal.
“Sign in” never finishes. Click “Cancel” and try again, or run shopify auth login in a terminal and open the project again.
The storefront shows but no element can be edited. The command was changed and no longer has --path $THEME, so the page has no marks. Put it back under “Change how it runs”.
For a theme that won’t start at all, see Troubleshooting.