September 7, 2026
I converted a Lovable store to a Shopify theme. Here are the four bugs nobody documents.
A lot of people are building storefronts in Lovable and then discovering there is no way to get them into Shopify. The zip won't import. There is no child theme option. The GitHub push doesn't help. Search Reddit for "Lovable Shopify" and you'll find the same question asked six different ways, usually with a freelancer in the comments offering to rebuild it by hand.
The reason is simple once someone says it plainly: Lovable exports a React app. A Shopify theme is Liquid, rendered on Shopify's servers, in a folder structure Shopify expects. There is no Liquid anywhere in that zip, so there is nothing to extract and nothing to import. Lovable's official Shopify integration doesn't close this gap either, because it goes the other direction: your React app stays on Lovable hosting and Shopify becomes the backend. That's headless, and it's a reasonable architecture, but it means your storefront never enters Shopify's theme system. No theme customizer. No theme apps. Your pixel and your checkout on two different domains.
So I did it the hard way. I took a Lovable coffee store I'd built, Ember & Oak, and turned it into a proper Online Store 2.0 theme: sections with schema settings, product data from Liquid objects, cart and checkout native. It works, and the result is editable by a non-developer in the theme customizer, which was the whole point.
It also took four days, and most of that was not the translation work. It was four failures that produce unhelpful errors and are documented almost nowhere. Every one of them passed shopify theme check with zero errors and then failed the moment the theme touched a real store. Every one of them cost me an afternoon. If you're attempting this yourself, here they are.
1. Shopify rejects {name} inside output tags
The context: a collection page heading in the original React app read Shop {name} with the collection's name dropped in. The natural Liquid translation is to keep the merchant-editable text as a setting with a placeholder in it and swap the placeholder at render time:
{{ section.settings.heading | replace: '{name}', collection.title }}theme check is fine with that. Shopify is not. On push:
sections/shop-header.liquid: Liquid syntax error (line 8): Variable '{{ section.settings.heading | replace: '{name}' was not properly terminated with regexp: /\}\}/What's happening: Shopify's Liquid parser is not the open-source one. It tokenises an output tag by scanning for the closing braces, and a literal } inside a quoted string within {{ ... }} throws that scan off. The '{name}' string ends the variable early, the parser looks for }}, finds something else, and rejects the file. Open-source Liquid handles the same line without complaint, which is why local tooling doesn't warn you.
The fix: do the substitution in a tag, where the parser is looking for %} instead, and output the result.
{%- assign heading = section.settings.heading | replace: '{name}', collection.title -%}
{{ heading }}Rule of thumb: never put a literal brace inside {{ }}. Anything with {x} placeholders gets assigned first.
2. georgia_n4 is not a valid font handle
Shopify's font system uses handles like helvetica_n4, where the suffix encodes weight and style. n4 means normal weight 400. It looks like a naming convention you can extend to any font, and it isn't. Only fonts in Shopify's own library have handles, and Georgia is not one of them, despite being a web-safe font available in every browser since roughly forever.
How it surfaced: the original site used Georgia for its serif headings, so settings_schema.json got a font_picker with "default": "georgia_n4", the same pattern as the two Google fonts next to it. theme check passed. The push did not:
config/settings_schema.json: Section 3: setting with id="font_serif" default is invalidThat's the whole error. It doesn't say which default, or why, and the same file had two other font pickers that were fine.
The fix: only fonts in Shopify's library get a picker. Georgia came out of settings_schema.json entirely, and the theme's CSS keeps a plain Georgia, "Times New Roman", serif stack so the headings still render in Georgia; the merchant just can't swap that one font from the customizer. The Google fonts (Bebas Neue and Space Grotesk in that theme) kept their pickers because Shopify's library carries both.
The wider lesson: if your Lovable design uses a Google font or a system font, you cannot assume Shopify's font picker can represent it. Check the handle exists before wiring it into settings_schema.json, or use a plain CSS stack in your theme CSS and leave the font out of the customizer.
3. url settings must hold real paths, and their defaults are barely allowed at all
Two rules here, both undocumented where you'd look, both only enforced on push.
First, a url setting in a section schema can have a default of /collections or /collections/all and nothing else. The original hero had a secondary button linking to /story, so the schema got "default": "/story". theme check: zero errors. Push:
sections/hero-banner.liquid: Invalid schema: setting with id="secondary_button_link" default must be a string or datasource access pathThe message says "must be a string." It was a string. What it means is "must be one of the two strings we accept."
Second, the values you put in templates/*.json for url settings must be real paths. The source had a category grid whose links were slugs from a database, running-shoes, sneakers, and those went into the template JSON as-is:
templates/index.json: Setting 'link' must be a valid URLThis one is nastier than it sounds because the failure is not local. The rejected section file never uploads, and every template that references it then fails with a second error that looks like a different bug:
templates/index.json: Section type 'hero-banner' does not refer to an existing section fileChase that second message and you'll go looking for a typo in a section name that isn't there.
The fix: url defaults in schema are dropped unless they're one of the two allowed values; the original link goes in the setting's info text instead so the merchant can see what it was. Values in template JSON get a leading slash, or become a full https:// URL, or are left out.
If you're generating templates programmatically, validate url values before you push. It's a two-line check that saves an afternoon.
4. One bad file makes the entire theme preview fail
This is the one that cost me the most time, because the symptom is so much larger than the cause.
I was running shopify theme dev to render the theme locally against the dev store's data. One section file, the collection header from bug 1, had been rejected on upload. What you see is not a broken collection page. You see this on every route, including the home page and the product pages that never touch that file:
Upload Errors
sections/shop-header.liquid
Liquid syntax error (line 8): Variable '{{ section.settings.heading | replace: '{name}' was not properly terminated with regexp: /\}\}/That's the whole page. Grey background, red banner, nothing else. My first assumption was that the theme was fundamentally broken, and I spent an hour looking at the layout file.
What's actually wrong: theme dev doesn't render your local files. It uploads them to a theme on Shopify and proxies the rendered pages back. If any file is rejected, the CLI shows the upload error overlay instead of rendering anything, on every route, until the file is fixed. The one bad file isn't taking down the theme; the CLI is refusing to show you a theme it knows is incomplete.
The fix: push first, and read the JSON.
shopify theme push --unpublished --jsonThe output has a theme.errors object listing every rejected file with Shopify's actual message. That's the only place these errors are spelled out. theme check will not catch any of the four in this post, because it checks Liquid syntax and best practices, not the rules Shopify's upload endpoint enforces.
The practical advice: treat theme push --json as the real validator. Run theme check first for the cheap stuff, then push, then read errors, then open theme dev. Never the other way round.
What I'd tell you if you're about to try this
Rebuilding a Lovable site as a Shopify theme is not conceptually hard. The markup is already written, the Tailwind classes carry over almost unchanged, and translating JSX into Liquid is mechanical work. If you know Liquid, you can do it.
The hard part is that Shopify's theme system has a lot of rules that only announce themselves when you break them, and the errors rarely point at the actual problem. Four afternoons of my week went to the four issues above, and none of them was in the docs I was reading at the time.
Two things worth knowing before you start:
- Don't bring your database. If your Lovable project has a Supabase table of products, that data has to move into Shopify and then stop existing in Supabase. Two systems that both believe they own inventory is a much worse problem than a zip that won't import.
- Make everything a setting. The reason to do this at all is that the merchant can edit their own store afterward. If you hardcode the headline into the section markup, you've built them a site they have to call you to change, and you've reproduced the exact problem they were trying to escape. Every headline, image, link, and button label goes in
{% schema %}.
Why I wrote this
I'm building Reliquify, which automates this conversion. It exists because I hit these four things one at a time and thought nobody should have to.
If you're stuck on a Lovable to Shopify move, the four fixes above are yours to use whether or not you ever look at what I'm building. And if you've hit a fifth one I haven't, I'd like to hear about it.
Nick Johnson — nickjohnson.builds@gmail.com
Reliquify
Reliquify automates this whole conversion and checks for all four of these bugs before you see a preview. Convert your Lovable site into a real Shopify theme.
Convert your site — $99