Magento with React, Vue or Next.js
Headless commerce with Magento GraphQL promises flexibility. In practice, Apollo Client configuration, type-safe code generation, SSR/SSG caching and fragment design decide whether the frontend stays fast and maintainable or turns into a complexity trap.
Table of Contents
- 1. Headless commerce: what the architectural promise means
- 2. Apollo Client vs. URQL vs. fetch: making the right choice
- 3. Code generation: type-safe queries in TypeScript
- 4. Fragment colocation: queries close to the component
- 5. Caching in the headless frontend: Apollo, SSR and CDN
- 6. SSR and SSG with Next.js and Magento GraphQL
- 7. React vs. Vue vs. Next.js: what fits which project
- 8. Common mistakes in headless operation
- 9. Authentication and customer context in the headless frontend
- 10. Summary
- 11. FAQ
1. Headless commerce: what the architectural promise means
Headless commerce means the frontend is completely decoupled from the commerce backend. Magento serves as the data and business logic layer, while the frontend is a standalone application that communicates over APIs. GraphQL is the preferred interface here because it lets the frontend request exactly the data a given page needs, without having to coordinate multiple REST endpoints. The architectural promise is: better frontend performance, freedom to choose whichever frontend technology fits, and the ability to evolve the frontend independently of Magento's upgrade cycle.
In practice, this promise only holds up when several prerequisites are met. The GraphQL schema must be stable and well documented so the frontend team can work independently. Caching needs to be thought through at multiple levels: the Apollo in-memory cache, an HTTP cache layer and the CDN edge cache. And the customer context (cart, login state, price group) must be transported securely and performantly through session tokens. Anyone who only swaps out the frontend without addressing these points simply trades well-known Luma problems for new headless problems.
2. Apollo Client vs. URQL vs. fetch: making the right choice
Apollo Client is the most comprehensive solution: a normalized in-memory cache, React hooks (useQuery, useMutation), optimistic updates, pagination utilities and a large ecosystem of libraries. For complex commerce frontends with a cart, product listings, search and customer context, Apollo Client is the strongest choice, even though the initial configuration is more involved. The normalized cache is especially valuable when the same products are loaded through different queries; Apollo deduplicates them automatically.
URQL is lighter, more modular and easier to configure. For projects that don't need every Apollo feature, URQL is a good alternative. Plain fetch with a custom wrapper function is sufficient for SSR/SSG contexts in Next.js, where queries only run once at page-generation time and no client-side cache is needed. The right choice depends on how complex the frontend is: the more client-side state and update logic, the more Apollo Client pays off.
# Fragment-based product query, collocated with the ProductCard component
# This query is generated and typed by GraphQL Code Generator
fragment ProductCardFragment on ProductInterface {
__typename
sku
name
url_key
small_image {
url
label
}
price_range {
minimum_price {
final_price {
value
currency
}
discount {
percent_off
}
}
}
}
query GetCategoryProducts(
$categoryId: String!
$pageSize: Int = 24
$currentPage: Int = 1
) {
products(
filter: { category_id: { eq: $categoryId } }
pageSize: $pageSize
currentPage: $currentPage
sort: { position: ASC }
) {
total_count
page_info {
current_page
page_size
total_pages
}
items {
...ProductCardFragment
}
}
}
3. Code generation: type-safe queries in TypeScript
GraphQL Code Generator analyzes the Magento schema together with the queries and fragments defined in the frontend, and generates fully type-safe TypeScript types and React hooks from them. The result: instead of manually maintaining interface definitions for every query response, types are derived automatically from the schema. If Magento renames a field or changes a type in the next release, code generation fails immediately, which means the breaking change becomes visible in the frontend before it causes problems in production.
Configuring GraphQL Code Generator for Magento requires downloading the schema. The Magento GraphQL schema can be obtained through the introspection endpoint (POST /graphql with the introspection query) or provided as an SDL file. For teams, an automatic schema download in the CI pipeline that runs on every Magento version update is recommended. That way generated types stay in sync with the actual Magento schema, and discrepancies are caught in CI before a developer spends an hour debugging a type error.
4. Fragment colocation: queries close to the component
Fragment colocation is one of the most important principles for maintainable GraphQL frontends. The idea: every React component defines its own fragment describing the data it needs. The parent page component combines these fragments into a single query. When a component needs new data, it adds fields to its own fragment, and the query updates automatically. This eliminates the common situation where a central query file has to be maintained for the entire page's data needs, with developers hunting for which fields belong to which components.
Combined with the near-operation-file preset in GraphQL Code Generator, the generated hook file ends up right next to the query file. The entire data pattern, meaning component, fragment, generated hook and types, lives in a single directory. During a code review it's immediately obvious what a component renders and which data it requests. This pattern also scales well in larger teams: frontend teams can work on different page sections in parallel without creating query conflicts.
# Authentication: customer token flow in Magento GraphQL
# Step 1: generate token with credentials
mutation GenerateCustomerToken($email: String!, $password: String!) {
generateCustomerToken(email: $email, password: $password) {
token
}
}
# Step 2: use token in Authorization header for subsequent requests
# Authorization: Bearer <token>
# Step 3: fetch customer data with active session
query GetCustomerData {
customer {
firstname
lastname
email
addresses {
id
firstname
lastname
street
city
postcode
country_code
default_shipping
default_billing
}
orders(pageSize: 5) {
items {
number
order_date
status
total {
grand_total { value currency }
}
}
}
}
}
# Step 4: revoke token on logout
mutation RevokeCustomerToken {
revokeCustomerToken {
result
}
}
5. Caching in the headless frontend: Apollo, SSR and CDN
The Apollo in-memory cache normalizes all GraphQL responses by type and ID. In practice that means: if a product with SKU DEMO-001 is loaded in a category listing and later requested again on the product detail page, Apollo returns the cached version without another network request. This normalization works automatically for every type that has an id field. For Magento types that don't use a standard ID (such as product types that use sku instead of id), the Apollo cache needs to be adjusted with a custom keyFields configuration.
At the HTTP level, Magento GraphQL queries are fundamentally POST requests and aren't cached by HTTP caches. Persisted queries convert frequently used queries into GET requests with a hash as a parameter. These GET requests can be cached by CDNs and reverse proxies. Magento supports persisted queries natively for catalog data; queries without customer context can then be served from the CDN with very high cache hit rates. That reduces the load on Magento and significantly improves time to first byte.
6. SSR and SSG with Next.js and Magento GraphQL
Next.js offers three rendering strategies, each requiring a different GraphQL caching approach. Static Site Generation (SSG) with getStaticProps is a good fit for product detail pages and category pages without customer context: the GraphQL query runs at build time, and the page is delivered as static HTML. Incremental Static Regeneration (ISR) extends SSG with automatic refreshes after a configurable interval, which is ideal for category pages that don't change on every request but still need to stay current. Server-Side Rendering (SSR) is necessary whenever customer context affects the rendered data, for example personalized prices or store credit information.
One important caveat: Apollo Client is primarily designed for the browser. In Next.js SSR contexts, it's advisable to either use a server-only fetch pattern without Apollo, or use Apollo with @apollo/client/react/ssr and the renderToStringWithData function, which resolves all Apollo queries during server rendering. For most commerce pages, a simple fetch inside getStaticProps combined with a separate Apollo Client only in the browser is the clearer and more maintainable solution.
7. React vs. Vue vs. Next.js: what fits which project
The choice of frontend framework shapes the GraphQL client choice and the caching architecture. React with Apollo Client is the best-documented combination with Magento GraphQL and has the largest ecosystem support. Next.js builds on top of that and adds SSR/SSG and file-system routing, making it the natural choice for commerce projects where SEO and initial-load performance are critical. Vue 3 with URQL or the Vue Apollo wrapper is a solid choice for teams with existing Vue experience; the GraphQL patterns are the same, only the component API differs.
| Criterion | React + Apollo | Next.js | Vue 3 + URQL |
|---|---|---|---|
| SEO & SSR | Extra effort required | Natively supported | Requires Nuxt.js |
| Caching | Normalized cache | ISR + CDN | Simple cache |
| Magento docs | Extensive | Extensive | Fewer examples |
| Code gen support | Full | Full | Full |
| Learning curve | Moderate complexity | Moderate complexity | Easy start |
For new headless Magento projects, Next.js is currently the default recommendation: the combination of SSG for catalog pages, SSR for personalized pages and native image optimization support makes it the strongest commerce framework in the React ecosystem. Vue teams typically settle on Nuxt.js and URQL, applying the same GraphQL principles.
8. Common mistakes in headless operation
The most common mistake: reloading all data from Magento on every request without using any caching layers. Category pages that load the entire product list fresh from Magento on every request create unnecessary load and poor TTFB numbers. The fix is a clear separation by data type: static catalog data gets cached (ISR, CDN), while data tied to the customer session is never cached.
The second common mistake is loading too many fields in a query. GraphQL queries that pull in hundreds of fields because the frontend might theoretically need them place unnecessary load on Magento resolvers. Fragment colocation solves this structurally: every component only requests the fields it actually renders. The third mistake concerns handling customer tokens: customer tokens stored in localStorage are vulnerable to XSS. For commerce applications, HttpOnly cookie storage is the safer alternative, though it requires CORS configuration on the Magento server.
9. Authentication and customer context in the headless frontend
Magento GraphQL uses bearer token authentication. A customer token is issued through the generateCustomerToken mutation and must be sent with every subsequent request as an Authorization: Bearer <token> header. Tokens have no built-in expiry in stock Magento, so validity has to be controlled through configuration or a custom module. When implementing this in the frontend, it must be guaranteed that queries containing customer data are never cached, neither in the Apollo in-memory cache for other users nor in the CDN.
The cart in a headless frontend is a particularly critical point. Magento distinguishes between guest carts (identified by a cart ID string) and customer carts (linked to the customer token). On login, the guest cart must be merged into the customer cart (mergeCarts mutation). This logic is error-prone and needs to be tested explicitly, especially in combination with browser tab switching and parallel sessions. A well-structured state management layer for the cart context matters more than the technical GraphQL implementation itself.
# Cart management in headless Magento: guest to customer merge
# Step 1: create guest cart
mutation CreateGuestCart {
createEmptyCart
}
# Returns: "abc123" (store as cookie or localStorage)
# Step 2: add items to guest cart
mutation AddToGuestCart($cartId: String!, $sku: String!, $qty: Float!) {
addSimpleProductsToCart(input: {
cart_id: $cartId
cart_items: [{ data: { sku: $sku, quantity: $qty } }]
}) {
cart {
id
totalQuantity
}
}
}
# Step 3: after login, merge guest cart into customer cart
mutation MergeCarts($guestCartId: String!, $customerCartId: String!) {
mergeCarts(
source_cart_id: $guestCartId
destination_cart_id: $customerCartId
) {
id
totalQuantity
itemsV2 {
items {
product { sku name }
quantity
}
}
}
}
10. Summary
Headless commerce with Magento GraphQL is a mature architecture that delivers real benefits when implemented carefully: frontend independence, precise data fetching and better performance through multi-tier caching. The critical success factors are: type-safe code generation with GraphQL Code Generator, fragment colocation for maintainable queries, clearly separated caching strategies for static and customer-related data, and robust cart-merge logic in the login flow.
Next.js is currently the strongest choice for new headless Magento projects because SSG, ISR and SSR are natively integrated and the caching architecture fits commerce requirements well. Apollo Client is the recommended GraphQL client library for complex commerce frontends. Simpler projects can start with URQL or plainer fetch patterns and migrate to Apollo later if needed.
GraphQL in Headless Frontends: the key takeaways at a glance
Framework choice
Next.js for SEO-critical commerce projects. React + Apollo for complex client-side state logic. Vue + Nuxt for Vue teams.
Code generation
GraphQL Code Generator with the Magento schema automates type-safe hooks and types. Breaking changes become visible in CI.
Caching
Static catalog data: ISR + CDN. Customer data: never cache. Configure the Apollo cache with keyFields for Magento types.
Cart & Auth
Store the guest cart ID in a cookie. Test mergeCarts on login explicitly. Customer token in an HttpOnly cookie, never in localStorage.