How to Build a Shopify App From Scratch
Building a Shopify app from scratch is not “clone the template and add a page.” You are attaching software to a platform that already owns catalog, checkout, and a large part of Admin. The work is to pick **one job**, pick **who installs it**, then earn the right to touch merchant data without lying in the happy path.
This is a how-to for founders and small teams. It is a pillar for the sequence, not a substitute for Shopify’s current documentation. CLI commands, template names, and the recommended framework change. **Check Shopify’s partner and app docs for the stack they recommend this quarter.** Remix and Polaris have been the usual Admin pairing; treat that as a starting point, not a forever contract.
Cost sits with the rest of custom work in [custom software development cost in 2026](/blog/custom-software-development-cost-2026). Distribution as a business choice lives in [custom Shopify apps](/blog/custom-shopify-apps)—this article uses that choice as a **fork in the build**, not a second comparison essay. If the job is a merchant-specific workflow, the live piece [custom Shopify apps](/blog/custom-shopify-apps) is the “when to commission” argument. Delivery: [Shopify apps](/services/shopify-apps).
The job comes before the Partner dashboard
Write one sentence: *After install, a staff member can complete ____ without a spreadsheet.* If you cannot fill the blank, you do not have an app. You have a pile of Shopify APIs.
Good first jobs:
Staff apply a pricing rule that Shopify’s native tools do not express. A bundle or kit is sold in a way the catalog cannot represent honestly (see [bundle app development](/blog/custom-shopify-apps) when that *is* the product). Subscriptions, skip, and pause for a catalog Shopify billing features do not cover the way you sell ([subscriptions](/blog/custom-shopify-apps)). An internal ops screen: approvals, wholesale drafts, 3PL handoff.
Bad first jobs:
“A platform for all merchandising.” “We’ll do bundles, upsells, and loyalty.” “It should work like Klaviyo plus Recharge plus a custom ERP.”
An honest first release is one complete path. [MVP vs full product](/blog/custom-software-development-cost-2026) applies here: fake MVP is half of checkout, half of Admin, half of a public listing.
Fork: who is this for?
Before you scaffold, decide distribution. The code shares a lot. The **release bar** does not.
**Path A — one merchant (custom).** Install on stores you control. You still need auth, webhooks, and a host. You can skip App Store listing, public billing, and “works on every theme.” Scope is a conversation with operators who will UAT on real orders.
**Path B — many merchants (public).** You will need listing, review, billing, uninstall cleanup, and the data-request flows Shopify requires of listed apps. Your first architecture choice is tenancy: one install per shop, credentials isolated, no leaking shop A’s data into shop B’s session. That is product work, not a middleware afterthought.
**Path C — a few known merchants.** Check current Shopify docs for custom distribution / unlisted options. Do not invent a channel. The build looks like custom with extra install hygiene.
Write the path at the top of the repo README. Teams that skip this build a custom app and then try to screenshot it into the App Store.
What “from scratch” actually includes
Ignore marketing sites that start with a UI kit. The skeleton is:
Partner account, app record, and a development store that resembles the real merchant (plan, markets, checkout type). App credentials and OAuth (or the current install flow Shopify documents). Never paste secrets into a theme. A server that can receive webhooks and survive retries. A way to call the Admin API with the current recommended protocol (Shopify has pushed GraphQL; verify in docs, do not freeze REST from a 2018 blog post in a contract). An Admin UI the merchant will actually open—often Polaris so it feels like Shopify, often Remix if that is still the official template. Logging you can read at 11pm when a webhook storms.
Theme edits are not step one. Theme app extensions or storefront UI come after the Admin job is true, unless the job *is* the storefront.
Checkout UI is not step one unless the job cannot exist without it. Checkout is a regulated surface. Read current checkout extensibility docs. Do not start with a script tag “because it worked on a client in 2021.”
Scaffold, then throw away the demo job
Use Shopify’s current CLI and app template. That is the fastest way to get auth and a local tunnel right. Then delete the example resource they shipped. Leaving the demo in production is how you confuse merchants and reviewers.
**Remix (conceptual).** If the template is Remix, you are building a small full-stack app: loaders and actions talk to Shopify, sessions are shop-scoped, you deploy a Node-ish host. The value is the official path for cookies, app proxy, and embedded Admin. The risk is treating Remix as the product. Remix will not design your inventory writes.
**Polaris (conceptual).** Polaris is Admin language: pages, indexes, modals, banners. Use it so staff do not feel they left Shopify. Do not use it as a storefront design system. Storefront and checkout have other extension models; check docs.
**App Bridge / embedded app.** Embedded apps sit in Admin. That is usually what merchants want. Standalone apps exist; they cost more support because staff lose the Shopify chrome. Confirm the current embedding guidance before you fight iframes from memory.
If you need a stack decision later, [Shopify app tech stack](/blog/how-to-build-a-shopify-app-from-scratch) is the companion. Do not let a stack debate delay the job sentence.
Auth and the shop record
Every request that mutates a store must know **which shop** and **whether the token is still valid**. Store:
Shop domain Token (encrypted at rest) Scopes you actually received (not the ones you hoped for) Install / uninstall timestamps
Uninstall is a feature. Webhooks that fire after uninstall should no-op cleanly. Public apps also owe a story for data deletion when Shopify says the merchant asked. Custom apps still should not keep card-adjacent data you do not need.
Scopes: ask for what the job writes. A kitchen-sink scope list fails review and scares merchants. Adding a scope later is an install event; budget it.
Webhooks are the spine
If your app only polls Admin when someone opens a page, it will lie. Orders, products, app uninstalled, and whatever your job depends on should arrive as events. Shopify will retry. Your handler must be **idempotent**: processing the same event twice cannot double-write inventory or double-charge a selling plan.
Practically:
Verify the webhook the way current docs specify. Deduplicate on event id or a hash you persist. Queue heavy work; do not do ERP calls inside the HTTP request Shopify is waiting on. Alert on a queue that is backing up.
This is the difference between a tutorial and software. [Software that holds up](/blog/software-that-holds-up) on Shopify is mostly webhook and write honesty.
Admin UI: one complete path
Build the screen staff will use on a Tuesday:
Find the thing (order, product, contract, bundle). Do the job. See a result that matches Admin (or a clear error that names what failed).
Do not build a dashboard of charts with dummy numbers. Do not build six nav items. Polaris index + detail is enough for v1.
If the job needs a block on the product page, add a theme app extension after the Admin path works. Theme work without a source of truth in Admin is how you get two prices.
Writes, metafields, and not fighting Shopify
Prefer Shopify’s native objects when they already mean the right thing: variants, discounts, files, metafields with a defined type, selling plans when you are actually in subscriptions.
Metafields are not a database. They are labeled facts on Shopify objects. If you need queryable history, refunds, or a staff audit log, that lives in *your* database, keyed by shop.
Never invent a parallel catalog that staff must keep in sync “for now.” [When spreadsheets become the business system](/blog/when-spreadsheets-become-the-business-system) is the failure mode. An app that exports to Sheets as a *report* is fine. An app that requires Sheets to be the truth is not an app.
Checkout, storefront, POS: add only if the job requires it
**Storefront.** Theme app extensions and app blocks are the current-ish way to inject UI without a permanent Liquid fork. Headless storefronts are a different integration. Ask the merchant what they actually run.
**Checkout.** Shopify has been replacing older customization with checkout extensibility. Capabilities depend on plan and region. Read the current docs. Do not name APIs from memory in a sales deck. [Upsell app development](/blog/custom-shopify-apps) is the commercial companion when checkout *is* the product.
**POS.** A different surface, different constraints. If the job is till-first, say so at the fork; do not discover it in week six.
Hosting, environments, secrets
You need at least:
Local + a tunnel Shopify can reach for webhooks during development A staging app + staging store Production
Secrets in a password manager or host env, not in the repo. Rotate when a contractor leaves.
Logs: shop domain, request id, webhook id, error. Not raw tokens. Not customer PII you do not need.
This is still [custom software](/services/custom-software) with a Shopify-shaped boundary. Treat deploys like a product, not like FTP.
Test on a store that can hurt you
Use a development store with:
The same checkout type as production (especially if you touch checkout) Enough products, locations, and markets to be annoying A staff account that is not the developer
Walk the job with the merchant. Place a test order. Refund it. Uninstall and reinstall. If you write inventory, receive a shipment and sell the last unit.
Public apps: keep a list of theme and plan combinations you claim to support. “Works on Dawn” is not a test plan.
Public path: listing is a project
If you forked to public, listing is not a Friday task. Privacy policy, app listing copy, screenshots, a test plan for review, billing that matches what you show in the listing. Use [Shopify App Store submission checklist](/blog/how-to-build-a-shopify-app-from-scratch) when you get there. Review can send you back for scopes, broken uninstall, or a job that does not match the listing.
Do not submit a custom-only app with hardcoded shop logic and hope.
Sequence you can put on a wall
Job sentence and distribution fork. Partner app + lookalike development store. Official scaffold; delete the demo. Shop install record + scoped token. Webhooks for the objects you care about, with idempotency. Admin UI for the one path. Writes with failure states. Only then: theme / checkout / POS if required. Staging UAT with the merchant. Production, then a maintenance plan—API versions do not freeze ([maintenance cost](/blog/improving-software-you-already-have)).
Calendar, not only steps: [Shopify app development timeline](/blog/how-to-build-a-shopify-app-from-scratch).
FAQ
Do I have to use Remix?
No. You have to use a stack that can complete OAuth, webhooks, and embedded Admin the way Shopify currently expects. Remix is often the path of least resistance because it is what the template uses. If you pick something else, you own the glue. Check docs before you freeze it in a statement of work.
Can I build the first version as a theme app and “upgrade” later?
You can ship a theme snippet as a prototype. Call it a prototype. The moment you write orders or inventory, you need a server and webhooks. Upgrading a Liquid fork into an app is usually a rewrite. [App vs theme customization](/blog/custom-shopify-apps) is that decision.
How do I keep Shopify from breaking me?
You cannot. You can version your API usage, subscribe to partner changelogs, and budget maintenance. That is part of the product, not an insult to your architecture. ## Build the job, then the platform tax Shopify from scratch is a complete merchant path plus the hygiene the platform demands. If you want a studio to run that sequence with you, [how we work](/how-we-work) and [onboarding](/onboarding). Founder-led delivery is how we keep the fork honest: [founder-led delivery](/blog/founder-led-delivery). WhatsApp and support@rootoverzero.com are the public contacts.