# How to Integrate Magento with V0

- Tool: V0
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: April 2026

## TL;DR

To integrate Magento with V0 by Vercel, generate a Next.js storefront UI with V0, create API routes that call the Magento REST API using an admin or customer access token, store your Magento base URL and credentials in Vercel environment variables, and deploy. Your app can display product catalogs, shopping carts, and order histories from a Magento or Adobe Commerce backend.

## Build a Headless Magento Storefront with V0 and Next.js on Vercel

Magento powers over 250,000 e-commerce sites and is the dominant choice for enterprise B2B and large retail operations. Many businesses want the reliability of Magento as a backend combined with the performance and developer experience of a modern Next.js frontend — this is the headless commerce pattern. V0 can generate high-quality e-commerce UI components (product grids, filter sidebars, product detail pages, cart drawers, checkout flows) that you connect to your Magento backend via Next.js API routes.

Magento's REST API is comprehensive, covering the entire e-commerce lifecycle: product catalog management, category navigation, product search, shopping cart operations, customer accounts, order history, and checkout. The API supports both admin-level access (for fetching and managing catalog data) and customer-level access (for cart operations and order history tied to specific customer accounts). For a headless storefront, you typically use admin tokens server-side for catalog data and generate customer tokens server-side after customer authentication.

The Next.js and Magento combination is increasingly popular as the headless storefront pattern matures. Next.js's ISR (Incremental Static Regeneration) is ideal for product pages — you can statically generate product pages at build time and revalidate them periodically as Magento product data changes, combining the speed of static generation with the freshness of server-side rendering. V0 is particularly strong at generating the product listing page, product detail page, and shopping cart components that form the core of a headless storefront.

## Before you start

- A running Magento or Adobe Commerce instance — either self-hosted, Magento Cloud, or Adobe Commerce Cloud — with the REST API accessible from the internet
- Magento admin access to generate an Integration token — navigate to System → Integrations → Add New Integration, set required permissions, and note the Access Token provided
- Your Magento store base URL (e.g., https://your-magento-store.com) — the REST API is at {baseUrl}/rest/V1/
- A V0 account at v0.dev and a Vercel account for deploying the Next.js storefront
- Basic understanding of headless commerce — the Next.js frontend handles all rendering while Magento serves as the data API, with no Magento theme or frontend code deployed

## Step-by-step guide

### 1. Generate the Storefront UI Components with V0

Open V0 at v0.dev and describe the e-commerce components you want to build. Magento data maps well to standard e-commerce patterns — product listings, category navigation, product detail pages, and cart management. The key advantage of V0 for headless Magento is that you can describe exactly the UX you want without being constrained by Magento's Luma theme or PWA Studio defaults. Be specific about product card layouts, filter sidebar behavior, variant selectors (size/color pickers for configurable products), and cart drawer animation. Magento product objects include fields like name, sku, price, special_price (sale price), media_gallery_entries (images), extension_attributes (stock status), and custom_attributes (any configurable attributes your store has defined). Describe these fields to V0 so it generates components with the right data bindings. For the product listing page, mention that you want ISR-compatible rendering — the page component fetches products server-side rather than client-side, which works with Next.js ISR for fast, cacheable responses. For the cart, describe a slide-in drawer pattern that persists cart state in localStorage (or a cookie) and syncs with Magento's cart API. After generating, push to GitHub via V0's Git panel and connect to Vercel.

**Expected result:** A ProductGrid component renders in V0's preview with product cards, sale price display, stock badges, and Add to Cart buttons. The component accepts a products array prop and handles loading and empty states.

### 2. Create the Magento Authentication and Product API Routes

Create the Next.js API routes that authenticate with Magento and fetch product catalog data. Magento's REST API uses bearer token authentication — for admin operations like fetching the full catalog, you use an Integration access token generated in the Magento admin. For customer-specific operations (cart, orders), you generate a customer token by calling the customer token endpoint with the customer's email and password credentials. For a headless storefront, the most common pattern is using the Integration token server-side for all catalog operations (since these are public data) and handling customer authentication separately through your Next.js auth layer. The Magento product search endpoint (GET /rest/V1/products) supports complex filtering using searchCriteria parameters — you can filter by category, price range, attribute values, and text search using a URL query string format that Magento parses into its search framework. Product responses include all configured attributes, images, pricing rules, and stock information. The response is paginated with total_count for building pagination controls. For configurable products (products with size/color variants), additional calls to /rest/V1/configurable-products/{sku}/options and /rest/V1/configurable-products/{sku}/children fetch the variant options and child product SKUs.

```
// app/api/magento/products/route.ts
import { NextRequest, NextResponse } from 'next/server';

const MAGENTO_BASE_URL = process.env.MAGENTO_BASE_URL;
const MAGENTO_TOKEN = process.env.MAGENTO_INTEGRATION_TOKEN;

interface MagentoProduct {
  id: number;
  sku: string;
  name: string;
  price: number;
  status: number;
  type_id: string;
  media_gallery_entries?: Array<{ file: string; position: number; disabled: boolean }>;
  custom_attributes?: Array<{ attribute_code: string; value: string }>;
  extension_attributes?: { stock_item?: { qty: number; is_in_stock: boolean } };
}

function getMagentoImageUrl(baseUrl: string, file: string) {
  return `${baseUrl}/pub/media/catalog/product${file}`;
}

export async function GET(request: NextRequest) {
  if (!MAGENTO_BASE_URL || !MAGENTO_TOKEN) {
    return NextResponse.json(
      { error: 'Magento is not configured' },
      { status: 500 }
    );
  }

  const { searchParams } = new URL(request.url);
  const page = parseInt(searchParams.get('page') ?? '1', 10);
  const pageSize = parseInt(searchParams.get('pageSize') ?? '20', 10);
  const categoryId = searchParams.get('categoryId') ?? '';
  const searchQuery = searchParams.get('q') ?? '';

  // Build Magento searchCriteria query parameters
  const params = new URLSearchParams({
    'searchCriteria[currentPage]': page.toString(),
    'searchCriteria[pageSize]': pageSize.toString(),
    'searchCriteria[filter_groups][0][filters][0][field]': 'status',
    'searchCriteria[filter_groups][0][filters][0][value]': '1',
    'searchCriteria[filter_groups][0][filters][0][condition_type]': 'eq',
    fields:
      'items[id,sku,name,price,extension_attributes,media_gallery_entries,custom_attributes],total_count',
  });

  if (categoryId) {
    params.set('searchCriteria[filter_groups][1][filters][0][field]', 'category_id');
    params.set('searchCriteria[filter_groups][1][filters][0][value]', categoryId);
    params.set('searchCriteria[filter_groups][1][filters][0][condition_type]', 'eq');
  }

  if (searchQuery) {
    params.set('searchCriteria[filter_groups][2][filters][0][field]', 'name');
    params.set('searchCriteria[filter_groups][2][filters][0][value]', `%${searchQuery}%`);
    params.set('searchCriteria[filter_groups][2][filters][0][condition_type]', 'like');
  }

  try {
    const response = await fetch(
      `${MAGENTO_BASE_URL}/rest/V1/products?${params.toString()}`,
      {
        headers: {
          Authorization: `Bearer ${MAGENTO_TOKEN}`,
          'Content-Type': 'application/json',
        },
        next: { revalidate: 300 }, // Cache for 5 minutes
      }
    );

    if (!response.ok) {
      throw new Error(`Magento API error: ${response.status}`);
    }

    const data = await response.json() as { items: MagentoProduct[]; total_count: number };

    const products = (data.items ?? []).map((item) => {
      const salePrice = item.custom_attributes?.find(
        (a) => a.attribute_code === 'special_price'
      )?.value;

      const image = item.media_gallery_entries?.find((e) => !e.disabled && e.position === 1);

      return {
        id: item.id,
        sku: item.sku,
        name: item.name,
        price: item.price,
        salePrice: salePrice ? parseFloat(salePrice) : null,
        imageUrl: image ? getMagentoImageUrl(MAGENTO_BASE_URL!, image.file) : null,
        inStock: item.extension_attributes?.stock_item?.is_in_stock ?? true,
        stockQty: item.extension_attributes?.stock_item?.qty ?? 0,
      };
    });

    return NextResponse.json({
      products,
      totalCount: data.total_count,
      page,
      pageSize,
    });
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Unknown error';
    console.error('Magento API error:', message);
    return NextResponse.json(
      { error: 'Failed to fetch products', details: message },
      { status: 500 }
    );
  }
}
```

**Expected result:** GET /api/magento/products returns a normalized array of product objects with name, price, sale price, image URL, and stock status from your Magento catalog. Pagination and category filtering work via query parameters.

### 3. Implement Cart Operations with Magento Guest Cart API

Create the cart API routes that handle add-to-cart, update quantity, and checkout operations. Magento supports two cart types: guest carts (for anonymous shoppers) and customer carts (for authenticated customers). For a public storefront, start with guest carts — the flow is to create a cart (POST /rest/V1/guest-carts which returns a cart ID token), then add items to the cart using that token (POST /rest/V1/guest-carts/{cartId}/items). Store the cart ID in a browser cookie or localStorage so it persists across page loads. The cart ID is not sensitive data, but treat it with reasonable care since anyone with the cart ID can access and modify that cart. For the cart display, GET /rest/V1/guest-carts/{cartId} returns the full cart with items, quantities, prices, and totals. For checkout, the Magento REST API supports a complete checkout flow including shipping address collection, shipping method estimation, payment information submission, and order placement. For most V0 storefront projects, a simplified flow that directs customers to the Magento checkout page (at your Magento store URL) after adding items to the cart is the fastest path to a working checkout — deep Next.js checkout integration with Magento requires implementing the full checkout API sequence which is complex to build and test correctly. The cart REST API also supports coupon code application (PUT /rest/V1/guest-carts/{cartId}/coupons/{couponCode}) for promotion codes.

**Expected result:** The CartDrawer component slides in when items are added, showing product details, quantity controls, and the subtotal. The Proceed to Checkout button routes to the Magento checkout flow.

### 4. Configure Environment Variables and Deploy to Vercel

Push your code to GitHub and configure Magento credentials in Vercel. Open the Vercel Dashboard, select your project, and navigate to Settings → Environment Variables. Add MAGENTO_BASE_URL with your Magento store's base URL (without a trailing slash, e.g., https://your-magento-store.com), and MAGENTO_INTEGRATION_TOKEN with the Integration access token from Magento's admin panel (System → Integrations → your integration → Access Token). Neither variable should have the NEXT_PUBLIC_ prefix — Magento API calls should always be server-side to protect your integration token and avoid CORS issues. If you need the Magento URL accessible in client-side code (for image URLs), you can expose only the base URL as NEXT_PUBLIC_MAGENTO_BASE_URL since it's not sensitive. After adding environment variables for Production (and Preview if you want to test against a staging Magento instance), save and redeploy. Verify the deployment by opening your Vercel URL and checking that the product catalog loads from Magento. If you see CORS errors, confirm that all API calls are going through your Next.js API routes and not directly from the browser to Magento. Magento's REST API may need to have CORS configured if you're calling it from the client side, but the Next.js API route pattern avoids CORS entirely.

**Expected result:** The Vercel deployment succeeds and the product catalog loads real products from your Magento backend with images, prices, stock status, and working Add to Cart functionality.

## Best practices

- Use Magento's Integration token for server-side catalog reads — never expose this token in client-side code or prefix it with NEXT_PUBLIC_
- Use the Magento 'fields' query parameter to limit API response data to only the fields you need — this significantly reduces response size and improves performance
- Implement ISR (Incremental Static Regeneration) for product pages using Next.js generateStaticParams and revalidate — product data changes infrequently and static generation provides the best performance for product catalog pages
- Cache product API route responses with Next.js fetch revalidation (next: { revalidate: 300 }) to reduce Magento API load while keeping catalog data reasonably fresh
- Handle Magento's configurable products separately from simple products — configurable products have child SKUs with individual pricing and stock that must be fetched from the children endpoint
- Always route Magento API calls through Next.js API routes — calling Magento directly from the browser would require CORS configuration on Magento and expose your integration token
- For checkout, redirect to the native Magento checkout URL rather than building a custom checkout unless you need deep customization — Magento's checkout handles complex tax, shipping, and payment scenarios reliably

## Use cases

### Headless Product Catalog

A Next.js product listing page that fetches the Magento catalog via API routes and renders products with filtering, sorting, and pagination. ISR keeps product data current without full page rebuilds on every request.

Prompt example:

```
Create a product catalog page for an e-commerce store. Left sidebar has category filters (Electronics, Clothing, Accessories), price range slider, and brand checkbox filters. Main area shows products in a 4-column grid. Each product card has: product image placeholder, product name, brand name, price with a sale price crossed out if applicable, star rating with count, 'Add to Cart' button, and a wishlist heart icon. Top bar shows product count and sort dropdown (Price: Low to High, Newest, Best Sellers). Include pagination at the bottom. Use a clean modern e-commerce design.
```

### Product Detail Page

A rich product detail page pulling Magento's full product data including images, attributes, configurable options (size/color), stock status, and related products. Customers can select variants and add to cart directly from the page.

Prompt example:

```
Build a product detail page. Left side: large image with 4 thumbnail images below. Right side: product name (h1), SKU in gray, star rating row, price and sale price, a size selector with buttons (XS S M L XL), a color selector with colored circles, quantity input with +/- buttons, a large 'Add to Cart' button in blue, and an 'Add to Wishlist' link. Below the fold: a tabbed section with Product Description, Specifications (table), and Reviews. At the bottom: 'You Might Also Like' horizontal scroll of related product cards. Clean white design with bold typography.
```

### Customer Order History Dashboard

An authenticated customer portal showing past Magento orders with status tracking, order details, and reorder functionality. Customers can view their complete purchase history and track shipments without navigating Magento's own account portal.

Prompt example:

```
Design a customer account order history page. Header shows customer name and member-since date. Below: a list of orders sorted newest first. Each order row shows: order number, order date, total price, status badge (Processing/Shipped/Delivered in different colors), number of items, and 'View Details' and 'Reorder' buttons. Clicking 'View Details' expands an accordion showing individual line items with product names, quantities, and prices. Include a search bar to filter orders by order number. Use account dashboard styling with a left nav showing Account, Orders, Addresses, and Payment Methods.
```

## Troubleshooting

### Magento API returns 401 Unauthorized despite correct token

Cause: The Magento Integration token may have been regenerated, the integration may be inactive, or the token was created with insufficient API permissions. Token permissions in Magento are granular — the integration must have access to the specific API resources you're calling.

Solution: Log into Magento admin and navigate to System → Integrations. Find your integration and check that it's Active and that the Resource Access is set to 'All' or includes Products, Categories, and Cart endpoints. If the token was regenerated, update MAGENTO_INTEGRATION_TOKEN in Vercel. Trigger a redeployment after updating environment variables.

### Product images return 404 or broken image errors

Cause: The Magento image URL is constructed incorrectly, the media base URL is different from the store base URL, or the Magento media files are served from a CDN with a different domain.

Solution: Fetch the media base URL from the Magento store config endpoint (GET /rest/V1/store/storeConfig) — the response includes a base_media_url field that is the correct URL prefix for product images. Use this value instead of constructing image URLs manually from your MAGENTO_BASE_URL.

```
// Fetch actual media URL from Magento:
const config = await fetch(`${MAGENTO_BASE_URL}/rest/V1/store/storeConfig`, headers);
const { base_media_url } = await config.json();
const imageUrl = `${base_media_url}catalog/product${item.media_gallery_entries[0].file}`;
```

### searchCriteria filters return all products instead of filtering correctly

Cause: Magento's searchCriteria URL parameter format is complex and uses indexed arrays — incorrect indexing causes filters to be ignored silently rather than throwing errors.

Solution: Verify your searchCriteria URL parameters are correctly structured. The format uses bracketed notation: searchCriteria[filter_groups][0][filters][0][field]=status. Each filter group is ANDed together, filters within a group are ORed. Use a URL debugger to confirm the parameter string is correctly encoded before sending to Magento.

```
// Correct searchCriteria structure for category filter:
params.set('searchCriteria[filter_groups][0][filters][0][field]', 'category_id');
params.set('searchCriteria[filter_groups][0][filters][0][value]', categoryId);
params.set('searchCriteria[filter_groups][0][filters][0][condition_type]', 'eq');
```

### Product prices show as 0 or undefined for configurable products

Cause: Configurable products in Magento have a price of 0 at the parent level — the actual prices live on the child simple products. The parent configurable product's price field is often 0 or the minimum child price.

Solution: For configurable products (type_id === 'configurable'), fetch the child products using GET /rest/V1/configurable-products/{sku}/children to get individual variant prices and SKUs. Display the price range (min to max child price) or the minimum price as the starting price.

```
// Fetch children for configurable products:
if (item.type_id === 'configurable') {
  const children = await fetchMagento(`/rest/V1/configurable-products/${item.sku}/children`);
  const prices = children.map((c: {price: number}) => c.price).filter(Boolean);
  item.priceMin = Math.min(...prices);
  item.priceMax = Math.max(...prices);
}
```

## Frequently asked questions

### Should I use Magento's REST API or GraphQL API for a headless storefront?

For a new V0-generated headless storefront, Magento's GraphQL API is often better than REST — you can request exactly the fields you need in a single query, avoiding multiple REST calls and over-fetching. However, GraphQL support varies by Magento version and extension — Magento 2.4+ has good core GraphQL coverage, while older versions or heavily customized stores may have more complete REST coverage. This guide uses REST for compatibility, but you can use the same Next.js API route pattern with a POST to your Magento GraphQL endpoint instead.

### What's the difference between a Magento Integration token and a customer token?

An Integration token is generated in the Magento admin panel and provides server-to-server access with the permissions you configure. It's used for admin-level operations like catalog reads. A customer token is generated by calling the /rest/V1/integration/customer/token endpoint with a customer's email and password — it provides access to that specific customer's account data (orders, cart, wishlist). Your Next.js app uses Integration tokens server-side for catalog data and generates customer tokens server-side during customer login for account-specific operations.

### How do I handle Magento product images in a Next.js app?

Magento stores product images at {base_media_url}catalog/product{file_path}. Fetch the base_media_url from the Magento store config endpoint (GET /rest/V1/store/storeConfig) — don't assume it's the same as your store URL since many stores serve media from a CDN. Add your Magento image domain to the images.domains array in next.config.js to enable Next.js Image optimization for Magento product photos.

### Can V0 generate a complete Magento storefront, or only individual components?

V0 can generate all the individual page components you need — product listing pages, product detail pages, category navigation, shopping cart, and account pages — but wiring them together into a complete routing structure and connecting them to the Magento API requires manual configuration. V0 excels at generating the UI layer; use this guide's API route patterns to connect those components to live Magento data.

### Does Magento's API support product search with full-text search?

Yes — Magento's product search endpoint (GET /rest/V1/products/search or GET /rest/V1/products with searchCriteria) supports full-text search queries using Elasticsearch or OpenSearch as the search backend. Use the searchCriteria with name like '%query%' for simple name matching, or use the dedicated search endpoint for Elasticsearch-powered full-text search that covers product descriptions and attributes.

### How do I keep product pages fast with ISR and Magento as the backend?

Configure Next.js generateStaticParams to pre-generate your most important product pages at build time using Magento's product list API. Set a revalidate interval (e.g., 300 seconds for 5 minutes) on your product detail pages so they automatically refresh from Magento periodically. For large catalogs, focus ISR on your top-traffic products and use on-demand revalidation (revalidatePath or revalidateTag) to refresh pages immediately when Magento sends a webhook notification of product changes.

---

Source: https://www.rapidevelopers.com/v0-integrations/magento
© RapidDev — https://www.rapidevelopers.com/v0-integrations/magento
