D
P
0

Shopify & Liquid

Pushing to Shopify Wiped the Client's Admin-Uploaded Image? Pull `templates/*.json` Before You Edit

July 6, 2026·4 min read
Pushing to Shopify Wiped the Client's Admin-Uploaded Image? Pull `templates/*.json` Before You Edit

I was working on a client's Shopify theme, and the task was simple: remove an unused Reviews section from the About page. I edited templates/page.about.json, deleted the Reviews block, and pushed:

shopify theme push --only=templates/page.about.json

The push reported success, no errors. I opened the live About page, and the Reviews section was gone as expected. Done, I thought. Until a few hours later the client messaged me asking why the image had changed. The hero image on the About page, which had been a product photo they had chosen, was now back to the theme's default fallback, product-3in1-box.webp. The client had to re-upload their image manually through the admin.

My first instinct: did I delete the wrong block? I checked the diff — no. I had only touched the Reviews block. But the hero image, which I never went near, had changed too. It felt like my push had reached into something I never touched.

Why this happens

Here is what was actually going on, and it is a classic trap when a live theme is shared with a client who also edits through the Shopify theme editor.

When a client uploads an image or changes text through the theme editor, those changes do not land in the Liquid markup files. They are stored as settings inside templates/*.json (and config/settings_data.json), server-side. Settings like image_picker, text, and other block settings live in that JSON, not in the template code. So the hero image the client uploaded was really an image_picker field inside the server's copy of templates/page.about.json.

The problem: my local working copy did not have that field. My copy was still an older version, from before the client uploaded the image. When I edited that local JSON to remove Reviews and pushed, I overwrote the entire contents of the server's templates/page.about.json with my local version. The hero image_picker field holding the client's uploaded image was not in my local version, so Shopify fell back to the Liquid fallback, and up came product-3in1-box.webp.

The key thing to understand: a push is a file-overwrite operation, not a smart merge. The push does not know which changes are "mine" and which are "the client's". It just replaces the server file with the local one. Any admin-side work not present in my local copy is gone.

The fix

The core rule: before touching any file the client can edit through the editor, pull the server version first, then edit on top of it.

Before pushing anything that touches templates/*.json, config/settings_data.json, or a section with a media picker, I now always pull that specific file first with --nodelete:

shopify theme pull --path=<dir> --store=<store> --theme=<id> --only=templates/page.about.json --nodelete

The --nodelete matters so this pull does not remove other local files that are absent on the server. After pulling, I diff it to see what settings and blocks the client added:

git diff

That diff is where the hero image_picker field holding the client's image shows up — the one that was missing from my local copy. Only then do I make my edit, removing the Reviews block, on top of a version that already includes the client's work. Then push:

shopify theme push --only=templates/page.about.json

Now my push carries both the client's hero image and my edit at once, without overwriting anything valuable.

What is safe and what is not

What makes this trap slippery: not every file needs this pull-first ritual. Pure code files — Liquid markup, CSS, and JS — are safe to push directly. Why? Because the theme editor cannot change those files. The client cannot edit sections/hero.liquid through the editor, so there is no server-side work there for me to overwrite.

The dangerous files are specifically the ones that store values the client can change: templates/*.json, config/settings_data.json, and any section with settings of type image_picker, video, or file_reference. That is where the client's uploads and copy edits live, and that is where a plain push can wipe them.

The takeaway

On a theme shared with a client, templates/*.json and settings_data.json are not just mine — they are shared territory. The server is the source of truth for anything the client typed or uploaded through the editor. A push overwrites, it does not merge, so pushing without pulling first will silently wipe the client's admin-side work. Since this incident, my flow is always: pull --only=<file> --nodelete, diff to see what the client added, edit on top, then push. For pure code files I skip the ritual, but the moment an image_picker or any media picker is involved, I pull first without exception. One extra command beats explaining to a client where their photo went.