As Ontario businesses grow, some find that a standard Shopify theme no longer does everything they want. Perhaps they have years of content in WordPress that drives most of their search traffic. Perhaps they want a highly custom, app-like shopping experience. Or perhaps they operate several brands and want a consistent front-end framework across all of them.
This is where headless commerce enters the conversation. It's a powerful approach, but it's not right for everyone, and it introduces real costs and complexity. This article explains what headless commerce is, when it makes sense, and how Shopify can be combined with a Nuxt front end and WordPress content. The first half is for business owners; the second half gets more technical for developers and agencies.
What "headless" means
In a traditional Shopify setup, Shopify handles both:
- The back end: Products, inventory, customers, orders, checkout, and POS.
- The front end: The storefront customers see, built with a Shopify theme (Liquid templates).
In a headless setup, Shopify remains the back end, but the front end is built separately using another technology. The front end communicates with Shopify through APIs, primarily the Storefront API.
Importantly, Shopify POS, the admin, inventory, and checkout continue to work as normal. Headless changes how the online storefront is built, not how your stores operate.
Options on a spectrum
It helps to think of headless as a spectrum rather than an all-or-nothing choice:
- Standard Shopify theme: Fastest to launch, lowest maintenance. Modern Shopify themes, including the Horizon family with nested theme blocks, offer considerable flexibility without custom code.
- Customized theme: Custom sections, blocks, and app extensions within Shopify's theme system.
- Embedded commerce: Adding Shopify products and carts to an existing website (such as WordPress) using Shopify's embeddable tools, like the Buy Button or storefront web components.
- Hybrid: A Shopify theme for the store, with a separate site (such as WordPress) for content, linked through shared navigation and design.
- Fully headless: A custom front end (such as Nuxt or Shopify's own Hydrogen framework) powered by Shopify APIs, with content from Shopify or a CMS like WordPress.
Many businesses get most of the benefit from options 1 through 4 without the cost of option 5.
When headless makes sense
Headless may be worth considering if:
- You have a large content operation. Your WordPress site has years of articles, recipes, guides, or community content that drives traffic, and you want commerce integrated seamlessly into that experience.
- You need a highly custom experience. Product configurators, complex bundles, interactive tools, or app-like interfaces that are difficult in a theme.
- You run multiple storefronts or brands and want a shared design system and codebase.
- You have in-house or agency development capacity to build and maintain the front end.
- Performance and front-end control are strategic priorities and you have the expertise to achieve them.
When headless doesn't make sense
Headless is usually not the right choice if:
- You're early in your growth. Budget is better spent on products, marketing, and operations.
- You lack ongoing development resources. A headless storefront needs maintenance, updates, and hosting.
- Your needs can be met with a theme. Shopify's theme capabilities have improved significantly.
- You depend heavily on theme-based apps. Many Shopify apps inject features into themes. In a headless build, you may need to integrate each one through APIs or rebuild functionality.
- Your team needs to edit pages visually. Headless builds need a content editing solution, which requires planning.
Costs and trade-offs
Before committing, understand:
- Build costs: Custom development is a significant investment.
- Hosting: You'll need hosting for the front end (Shopify offers Oxygen hosting for Hydrogen; other frameworks use platforms like Vercel, Netlify, Cloudflare, or your own infrastructure).
- Maintenance: Framework updates, API version upgrades (Shopify releases API versions quarterly), security patches, and bug fixes.
- App compatibility: Some apps won't work out of the box.
- Analytics and tracking: You'll need to implement Shopify analytics events, consent handling, and marketing pixels properly.
- Checkout: Shopify's checkout is still used, so customers move from your front end to Shopify's hosted checkout. You can brand it, but it's a separate experience.
For an Ontario SME, a realistic assessment of total cost of ownership over three years is essential.
Accessibility and compliance still apply
A custom front end doesn't change your legal obligations. If your organization is subject to AODA website requirements, your headless storefront must meet them. CASL, privacy, and bilingual considerations also still apply. In a headless build, your development team, not a theme developer, is responsible for these.
---
For developers: Shopify + Nuxt + WordPress architecture
The rest of this article covers technical considerations for teams building headless Shopify storefronts with Nuxt (Vue) and WordPress.
A typical architecture
`` ┌───────────────────────────┐ │ Nuxt front end │ (SSR/hybrid rendering, Vue components) │ pages, components, cart │ └──────┬─────────────┬──────┘ │ │ ▼ ▼ ┌─────────────┐ ┌──────────────┐ │ Shopify │ │ WordPress │ │ Storefront │ │ REST API or │ │ API │ │ WPGraphQL │ └──────┬──────┘ └──────────────┘ │ ▼ ┌──────────────────────────────┐ │ Shopify checkout, admin, POS │ └──────────────────────────────┘ ``
- Shopify provides products, collections, pricing (including market-specific pricing), inventory availability, carts, customer accounts, and checkout.
- WordPress provides editorial content: blog posts, guides, landing pages, and store location pages.
- Nuxt combines both into a single experience with server-side rendering for SEO and performance.
Storefront API access
Shopify's Storefront API is a GraphQL API. You'll typically create access through the Headless channel or a custom app. There are two token types:
- Public access tokens for client-side requests.
- Private access tokens for server-side requests, which must never be exposed to the browser.
In Nuxt, keep private tokens in runtimeConfig (server-only) and proxy requests through Nitro server routes.
``ts // nuxt.config.ts export default defineNuxtConfig({ runtimeConfig: { shopifyStorefrontPrivateToken: '', // set via NUXT_SHOPIFY_STOREFRONT_PRIVATE_TOKEN public: { shopifyDomain: '', // e.g. your-store.myshopify.com shopifyApiVersion: '' // use a current stable Storefront API version } }, routeRules: { '/products/': { swr: 300 }, '/collections/': { swr: 300 }, '/blog/**': { swr: 600 }, '/cart': { ssr: false } } }) ``
```ts // server/utils/shopify.ts export async function shopifyFetch<T>(query: string, variables: Record<string, unknown> = {}) { const config = useRuntimeConfig() const { shopifyDomain, shopifyApiVersion } = config.public
return await $fetch<{ data: T; errors?: unknown }>( https://${shopifyDomain}/api/${shopifyApiVersion}/graphql.json, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Shopify-Storefront-Private-Token': config.shopifyStorefrontPrivateToken }, body: { query, variables } } ) } ```
```ts // server/api/products/[handle].get.ts export default defineEventHandler(async (event) => { const handle = getRouterParam(event, 'handle') const country = (getQuery(event).country as string) || 'CA' const language = (getQuery(event).language as string) || 'EN'
const query = #graphql query Product($handle: String!, $country: CountryCode, $language: LanguageCode) @inContext(country: $country, language: $language) { product(handle: $handle) { id title descriptionHtml featuredImage { url altText width height } variants(first: 50) { nodes { id title availableForSale price { amount currencyCode } } } } }
const { data } = await shopifyFetch<{ product: unknown }>(query, { handle, country, language }) if (!data?.product) throw createError({ statusCode: 404, statusMessage: 'Product not found' }) return data.product }) ```
``vue <!-- app/pages/products/[handle].vue --> <script setup lang="ts"> const route = useRoute() const { data: product } = await useFetch(/api/products/${route.params.handle}`, { query: { country: 'CA', language: 'EN' } })
useSeoMeta({ title: () => product.value?.title, ogImage: () => product.value?.featuredImage?.url }) </script> ```
Note the @inContext directive, which is how you request market-specific pricing and translated content. For a bilingual Ontario store, pass language: 'FR' on French routes (for example, with @nuxtjs/i18n handling locale prefixes) so product content matches the page language.
Carts and checkout
Use the Cart API mutations (cartCreate, cartLinesAdd, cartLinesUpdate, cartBuyerIdentityUpdate) and store the cart ID in a cookie. When the customer is ready, redirect to the cart's checkoutUrl, which sends them to Shopify's hosted checkout.
Set buyer identity (such as country) on the cart so taxes, duties, and shipping reflect the right market, which is especially important if you sell to both Canadian and U.S. customers.
Customer accounts
Shopify's Customer Account API is the current approach for authenticated customer experiences in headless storefronts, using OAuth-based authentication. Plan this early, since account features (order history, addresses, B2B buyer access) affect your architecture.
Pulling content from WordPress
WordPress can serve content through its built-in REST API or through WPGraphQL.
``ts // server/api/posts/index.get.ts export default defineEventHandler(async () => { const wpUrl = useRuntimeConfig().wpApiUrl // e.g. https://content.example.ca/wp-json/wp/v2 return await $fetch(${wpUrl}/posts, { query: { per_page: 12, _embed: 1 } }) }) ``
Useful patterns:
- Product references in WordPress content: Store Shopify product handles in custom fields (for example, with Advanced Custom Fields) and resolve them in Nuxt to render live product cards with current prices and availability.
- Shoppable content: Blog posts and guides that include "add to cart" components connected to the Shopify cart.
- Location pages: Manage store location content in WordPress or Shopify metaobjects, then render them in Nuxt with LocalBusiness structured data.
- Sanitization: Sanitize any HTML rendered from WordPress before using
v-html.
Alternatively, some teams use Shopify's own content features (metaobjects and the blog) to avoid running a separate CMS. If the WordPress investment is mainly legacy content, consider whether migration is simpler.
Caching and revalidation
- Use Nitro's route rules (
swr,isr, orcache) to cache product, collection, and content pages. - Subscribe to Shopify webhooks (such as product updates and inventory changes) and WordPress publish hooks to invalidate caches.
- Keep inventory-sensitive UI (like "only 2 left" or stock by location) fresh with client-side requests or short cache lifetimes.
- Never cache personalized data, such as carts or customer information, in shared caches.
Analytics and consent
- Implement Shopify's analytics events for headless storefronts so Shopify reports remain meaningful.
- Handle cookie consent properly and integrate with Shopify's Customer Privacy API.
- Ensure marketing pixels respect consent choices.
SEO considerations
- Use server-side or hybrid rendering for product, collection, and content pages.
- Generate sitemaps from both Shopify and WordPress data.
- Add hreflang for English and French versions.
- Implement Product, BreadcrumbList, and LocalBusiness structured data.
- Plan 301 redirects carefully if migrating from an existing site.
Accessibility
- Use semantic HTML in Vue components.
- Manage focus for modals, drawers (such as cart drawers), and route changes.
- Test with screen readers and keyboards as part of your QA process.
- Consider automated accessibility tests in CI.
Alternatives to evaluate
- Shopify Hydrogen (React-based) with Oxygen hosting is Shopify's own headless stack and may offer tighter integration.
- Theme app extensions and custom themes may meet requirements without going headless.
- Embedded commerce in WordPress using Shopify's embeddable components may suffice for content-led businesses.
Evaluate options against your team's skills. For Vue and Nuxt teams, Nuxt is a productive choice, but the Shopify-specific tooling ecosystem is smaller than Hydrogen's.
---
Decision checklist for business owners
- [ ] We've identified specific needs that a Shopify theme can't meet.
- [ ] We have budget for build, hosting, and ongoing maintenance.
- [ ] We have reliable development resources for the long term.
- [ ] We've confirmed which apps we rely on and how they'll work headless.
- [ ] We have a plan for content editing.
- [ ] We've planned for accessibility, privacy, and bilingual requirements.
- [ ] We've compared headless against hybrid and embedded options.
Key takeaways
- Headless commerce separates the storefront from Shopify's back end, while Shopify POS, checkout, and admin continue working normally.
- It suits businesses with large content operations, highly custom needs, or multiple brands, and with ongoing development resources.
- For many Ontario SMEs, a modern Shopify theme, hybrid setup, or embedded commerce offers most of the benefit at lower cost.
- Nuxt works well as a headless front end using the Storefront API through server routes, with WordPress providing content.
- Plan carefully for carts, customer accounts, caching, analytics, SEO, and accessibility.
APIs, frameworks, and Shopify features evolve quickly. Always consult current Shopify developer documentation and use a supported Storefront API version.