Build a custom app or use an existing one?
Before you build: audit whether Print It My Way, Bold Options, Infinite Options, or Zakeke already handles your workflow. Custom apps take 3-6 months to build and 30-60 hrs/month to maintain. If your only "custom" requirement is a specific printer's file spec, integrate that spec through an existing app's webhook rather than building from scratch. Custom is warranted when: (1) you have proprietary IP you don't want in a third-party app's database, (2) you need integrations no app supports (weird ERP, custom fulfillment routing across 5+ partners), or (3) you're a Shopify agency selling a productized service.
The modern 2026 stack
App Router: Remix (Shopify's official framework)
Language: TypeScript
DB: Prisma + Postgres (or Cloudflare D1 for edge)
UI: Polaris (admin UI kit)
Storefront: Theme App Extensions (block-based UI)
Pricing: Cart Transform Functions (server-side JS)
Checkout: Checkout UI Extensions
Hosting: Shopify Hydrogen/Oxygen or Vercel or Fly.io
Bootstrapping with the CLI
npm create @shopify/app@latest -- --template remix
cd my-personalization-app
npm run dev
The CLI scaffolds a Remix app with authentication, webhook subscription, App Bridge, Polaris, and Prisma schema pre-wired. Login as a partner, connect to a development store, and iterate.
Theme App Extensions for storefront UI
Theme App Extensions replace the old "hidden product with special metafields" hack. You ship a JS + Liquid block that merchants add to their product page via the theme editor. Your app renders the personalization option selector inside the block.
extensions/personalizer-block/
├── shopify.extension.toml
├── blocks/personalizer.liquid
├── assets/personalizer.js
└── locales/en.default.json
Your personalizer.js reads the product's variant metafields (personalization spec, DPI, max chars) and renders the appropriate input fields. On add-to-cart, it attaches personalization data as line-item properties.
Cart Transform Function for pricing
// extensions/pricing/src/index.ts
export function run(input) {
const operations = [];
for (const line of input.cart.lines) {
const propMap = Object.fromEntries((line.attributes ?? []).map(a => [a.key, a.value]));
let extra = 0;
if (propMap["Custom text"]) extra += 3.99;
if (propMap["Photo"]) extra += 6.99;
if (extra > 0) {
operations.push({
update: {
cartLineId: line.id,
price: { adjustment: { fixedPricePerUnit: {
amount: (line.cost.amountPerQuantity.amount + extra).toFixed(2)
}}}
}
});
}
}
return { operations };
}
Checkout UI Extensions for post-purchase
Show a personalization proof, upsell related items, or capture additional print instructions during Shopify's checkout flow. Extension runs in a sandboxed iframe with access to cart + buyer identity via the Checkout Extension API.
Database schema for personalization data
// prisma/schema.prisma
model Shop {
id String @id
domain String @unique
accessToken String
plan String @default("free")
createdAt DateTime @default(now())
optionSets OptionSet[]
}
model OptionSet {
id String @id @default(cuid())
shopId String
productGid String // gid://shopify/Product/...
spec Json // { options: [...], pricing: {...} }
createdAt DateTime @default(now())
shop Shop @relation(fields: [shopId], references: [id])
@@index([shopId, productGid])
}
Hosting
| Hosting option | Best for | Free tier? | Notes |
|---|---|---|---|
| Shopify Oxygen | Hydrogen apps only | Yes (Plus) | Edge, integrated CI, Shopify-owned |
| Vercel | Next.js / Remix | Yes (hobby) | Best DX; edge functions; usage-based pricing |
| Fly.io | Any Docker app | Yes (small tier) | Regions worldwide, cheap DB |
| Cloudflare Workers | Edge-first, JS-only | Yes | D1 for DB, KV for sessions |
| Render / Railway | Traditional web apps | Yes (small tier) | Simple Postgres + web |
App Store listing (public apps)
If distributing publicly: Partners → Apps → Create app → follow the App Store checklist (screenshots, video demo, GDPR compliance webhooks, uninstall webhook, mandatory data-request webhook, privacy policy, support email). Review takes 3-8 weeks; the top rejection reason is missing GDPR webhooks (customers/data_request, customers/redact, shop/redact).
Billing (App Subscription API)
Use Shopify's App Subscription API for recurring pricing. Shopify handles the payment flow inside the admin; you charge $X/month and Shopify remits (minus their revenue share, currently 15% up to $1M/year then 20%).
Frequently asked questions
What's the modern stack for a custom Shopify personalization app?
Remix + TypeScript + Prisma + Postgres + Polaris for the admin UI, Theme App Extensions for the storefront UI, Cart Transform Functions for pricing, Checkout UI Extensions for checkout, hosted on Oxygen/Vercel/Fly.
Should I build a custom app or use an existing one?
Existing apps first — Print It My Way, Bold Options, Infinite Options, Zakeke cover 90% of personalization needs. Build custom only when you have proprietary IP, need unsupported integrations, or run an agency selling a productized service.
How do I bootstrap a Shopify app project?
npm create @shopify/app@latest -- --template remix — Shopify's CLI scaffolds a Remix app with auth, webhooks, App Bridge, Polaris, and Prisma pre-wired.
Where does the personalization storefront UI live in a custom app?
In a Theme App Extension — a Liquid block + JS bundle the merchant adds to their product page via the theme editor. Replaces the old hidden-product hack and gives you first-class control over the option selector UI.
How do I price personalization add-ons in a custom app?
Cart Transform Function — a server-side JavaScript function that runs at checkout and modifies line-item prices based on cart attributes. Ships as an extension inside your app.
Where should I host my custom Shopify app?
Vercel or Fly.io for most cases — best DX, generous free tier. Shopify Oxygen if you're building on Hydrogen (Plus stores). Cloudflare Workers if you need edge-first and JS-only.
What GDPR webhooks does my Shopify app need?
Three mandatory webhooks: customers/data_request, customers/redact, shop/redact. Missing any of them is the #1 App Store rejection reason.
How does Shopify's revenue share on app billing work?
Shopify takes 15% of app subscription revenue up to $1M/year per app, then 20% above that. Payment flow happens inside Shopify admin; you receive net-of-share monthly.
Related reading
- Admin API for options — GraphQL reference
- Checkout Extensions for options — checkout UI
- Cart Transform Functions guide — pricing logic
- Headless commerce options — Storefront API architecture
Try it free on Shopify
Print It My Way's permanent Free plan is built on this exact stack — Remix + Cart Transform + Theme App Extensions — so you get production-grade personalization without building from scratch.
Install Print It My Way