To build a headless storefront with PrestaShop and V0 by Vercel, enable PrestaShop's Web Services API on your PrestaShop installation, create a Next.js API route that fetches products and categories via REST, and generate the storefront UI with V0. Store your PrestaShop API key in Vercel environment variables. This pattern gives PrestaShop store owners a modern, fast Next.js frontend while keeping their existing PrestaShop backend for inventory and order management.
Build a Modern Headless Storefront on Top of PrestaShop with V0
PrestaShop powers over 300,000 online stores worldwide with its free, open-source e-commerce platform. However, PrestaShop's default theme can feel dated, and achieving a truly modern shopping experience often requires significant theme customization. The headless approach — using PrestaShop purely as the backend (inventory, pricing, orders) while V0 generates a modern Next.js frontend — gives store owners the best of both worlds.
PrestaShop's built-in Web Services API provides RESTful access to all store data: products, categories, customers, orders, inventory, prices, and more. The API returns XML by default but also supports JSON format with the `output_format=JSON` parameter. Authentication uses HTTP Basic auth with a key you generate in the PrestaShop admin panel.
The headless pattern is particularly powerful for stores that have outgrown PrestaShop's template system. Your V0-generated Next.js storefront can be as visually unique as any design, deployable to Vercel's global CDN for fast page loads anywhere in the world, while PrestaShop continues handling the business logic — pricing rules, promotions, tax calculations, shipping rates, and order fulfillment — that would be complex to rebuild.
Integration method
PrestaShop integrates with V0 apps through Next.js API routes that call PrestaShop's built-in Web Services REST API. The Web Services API uses HTTP Basic auth with an API key generated in the PrestaShop admin panel. Your API route fetches products, categories, prices, and inventory from PrestaShop and returns normalized data to the V0-generated storefront. The checkout process typically redirects users to PrestaShop's native checkout or uses a separate payment gateway.
Prerequisites
- A running PrestaShop installation version 1.7 or later (self-hosted or cloud)
- Admin access to your PrestaShop back office to enable Web Services and generate an API key
- Your PrestaShop store URL (e.g., https://your-store.com)
- A V0 project exported to GitHub and deployed on Vercel
- Basic understanding of Next.js API routes and REST APIs
Step-by-step guide
Enable PrestaShop Web Services and Generate an API Key
Enable PrestaShop Web Services and Generate an API Key
Log into your PrestaShop back office (usually at your-store.com/admin). Navigate to Advanced Parameters → Webservice. At the top of the page, toggle 'Enable PrestaShop Webservice' to 'Yes' and click 'Save'. Next, click 'Add new webservice key' (the plus button). PrestaShop will generate an API key for you — or you can click 'Generate' to create a new one. Copy this key immediately as you'll add it to Vercel environment variables. For the key permissions, select the appropriate resources. For a read-only storefront, enable GET permission on: `products`, `categories`, `product_features`, `product_feature_values`, `combinations`, `images`, `stock_availables`, `taxes`, `tax_rule_groups`. If you want to handle order creation from the headless frontend, also enable POST/PUT on `carts`, `orders`, and `customers`. Save the webservice key. To verify it works, open your browser and navigate to `https://your-store.com/api/?ws_key=YOUR_API_KEY&output_format=JSON` — you should see a JSON response listing all available API endpoints. If you get a 404, ensure the webservice is enabled. If you get a 403, check the key permissions. PrestaShop's Web Services API base URL is `https://your-store.com/api/` and all endpoints are appended directly: `/api/products`, `/api/categories`, etc. The `output_format=JSON` parameter is required to get JSON instead of XML — always include it.
Create a product grid page for an e-commerce store. Show a responsive grid of product cards with product image, product name, current price in bold, 'on sale' badge if there's a discount, and an 'Add to Cart' button. Add a sticky top navigation with category links and a search input. Show loading skeletons while products are fetched.
Paste this in V0 chat
Pro tip: PrestaShop's API key is shown only once when first created. If you lose it, you'll need to generate a new one in Advanced Parameters → Webservice → your key → Edit.
Expected result: PrestaShop Web Services enabled with an API key that has GET permissions on products and categories. Browser test at your-store.com/api/?ws_key=KEY&output_format=JSON returns valid JSON.
Create the PrestaShop API Route
Create the PrestaShop API Route
Create a Next.js API route at `app/api/prestashop/route.ts` that proxies requests to your PrestaShop Web Services API. PrestaShop uses HTTP Basic authentication where the API key is the username and the password is empty — encode `{API_KEY}:` in Base64 for the Authorization header. PrestaShop's JSON API responses nest data under a key matching the resource name. For example, `GET /api/products?output_format=JSON` returns `{ products: [{ id: 1, ... }] }`. For a single product, `GET /api/products/1?output_format=JSON` returns `{ product: { id: 1, ... } }`. Product data in PrestaShop is structured differently from modern e-commerce APIs. Key fields: `id` (integer), `name` (array keyed by language ID), `description` (multilingual), `price` (tax-excluded, as decimal string), `quantity` (from stock_availables link), `active` (1 or 0). Product images require separate requests to `/api/images/products/{product_id}/{image_id}`. For the storefront, the most efficient approach is to fetch products with `display=[id,name,price,description_short,active]` to limit which fields are returned (reduces payload from ~100 fields to just what you need). Use `filter[active]=1` to only return published products. Use `limit=12&page=1` for pagination. PrestaShop stores multilingual content as arrays: `name: [{ id: '1', language: { id: '1' }, value: 'Product Name' }]`. Extract the display name by finding the entry for your target language ID (usually 1 for English) using `name.find(n => n.language.id === '1')?.value`.
1// app/api/prestashop/route.ts2import { NextRequest, NextResponse } from 'next/server';34const PS_URL = process.env.PRESTASHOP_URL!; // e.g., https://your-store.com5const PS_KEY = process.env.PRESTASHOP_API_KEY!;6const LANG_ID = process.env.PRESTASHOP_LANG_ID || '1'; // default language78function getAuthHeader() {9 return 'Basic ' + Buffer.from(`${PS_KEY}:`).toString('base64');10}1112function extractLangValue(field: any, langId = LANG_ID): string {13 if (typeof field === 'string') return field;14 if (!Array.isArray(field)) return '';15 return field.find((f: any) => String(f?.language?.id ?? f?.attrs?.id) === langId)?.value || '';16}1718async function prestashopFetch(endpoint: string, params?: Record<string, string>) {19 const url = new URL(`${PS_URL}/api${endpoint}`);20 url.searchParams.set('output_format', 'JSON');21 if (params) Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));2223 const res = await fetch(url.toString(), {24 headers: { Authorization: getAuthHeader() },25 next: { revalidate: 300 },26 });27 if (!res.ok) throw new Error(`PrestaShop API error: ${res.status}`);28 return res.json();29}3031export async function GET(request: NextRequest) {32 const { searchParams } = new URL(request.url);33 const resource = searchParams.get('resource') || 'products';34 const id = searchParams.get('id') || '';35 const page = searchParams.get('page') || '1';36 const categoryId = searchParams.get('categoryId') || '';3738 try {39 if (resource === 'products') {40 const params: Record<string, string> = {41 'display': '[id,name,description_short,price,id_category_default,active]',42 'filter[active]': '1',43 'limit': `12,${(parseInt(page) - 1) * 12}`,44 };45 if (categoryId) params['filter[id_category_default]'] = categoryId;4647 const data = await prestashopFetch('/products', params);48 const products = (data.products || []).map((p: any) => ({49 id: p.id,50 name: extractLangValue(p.name),51 shortDescription: extractLangValue(p.description_short),52 price: parseFloat(p.price).toFixed(2),53 imageUrl: `${PS_URL}/api/images/products/${p.id}/1?ws_key=${PS_KEY}`,54 categoryId: p.id_category_default,55 }));56 return NextResponse.json({ products });57 }5859 if (resource === 'product' && id) {60 const data = await prestashopFetch(`/products/${id}`, {61 display: 'full',62 });63 const p = data.product;64 return NextResponse.json({65 id: p.id,66 name: extractLangValue(p.name),67 description: extractLangValue(p.description),68 price: parseFloat(p.price).toFixed(2),69 reference: p.reference,70 weight: p.weight,71 });72 }7374 if (resource === 'categories') {75 const data = await prestashopFetch('/categories', {76 display: '[id,name,active,id_parent]',77 'filter[active]': '1',78 });79 const categories = (data.categories || []).map((c: any) => ({80 id: c.id,81 name: extractLangValue(c.name),82 parentId: c.id_parent,83 }));84 return NextResponse.json({ categories });85 }8687 return NextResponse.json({ error: 'Invalid resource' }, { status: 400 });88 } catch (err: any) {89 return NextResponse.json({ error: err.message }, { status: 500 });90 }91}Pro tip: PrestaShop product images are served directly from the PrestaShop API URL. For better performance, consider using Next.js Image with the PrestaShop URL added to remotePatterns in next.config.js, or sync images to Vercel Blob storage.
Expected result: The API route returns normalized product and category data from PrestaShop with correct multilingual name extraction and image URLs.
Add Vercel Environment Variables
Add Vercel Environment Variables
Navigate to your Vercel project → Settings → Environment Variables. Add the required variables for your PrestaShop connection. Add `PRESTASHOP_URL` — the base URL of your PrestaShop store, without a trailing slash (e.g., `https://your-store.com`). This is the URL customers use to access your store. If your store is on a subdomain like `shop.yourcompany.com`, use that URL. Add `PRESTASHOP_API_KEY` — the API key you generated in PrestaShop's Advanced Parameters → Webservice section. This is a 32-character alphanumeric string. Set it without the `NEXT_PUBLIC_` prefix as it must remain server-side only. Optionally add `PRESTASHOP_LANG_ID` — the language ID for your default display language. In PrestaShop, go to International → Languages to see your language IDs. English is typically ID 1. If your store is multilingual or a non-English store, set this to the correct language ID for your primary display language. For staging and testing, if you have a PrestaShop development or staging environment, create separate environment variables for Vercel's Preview deployments pointing to the staging PrestaShop instance. This prevents test orders and test product changes from appearing in production. After saving, redeploy and test the integration by visiting `/api/prestashop?resource=products` in your browser — you should see your actual PrestaShop products in the JSON response.
Pro tip: PrestaShop API keys are sensitive — they can read all order and customer data if given full permissions. Create a separate API key with read-only permissions for the storefront frontend, and a different key with write permissions only if you need to create orders from the headless frontend.
Expected result: PRESTASHOP_URL, PRESTASHOP_API_KEY, and PRESTASHOP_LANG_ID set in Vercel environment variables. Test API call returns product data.
Build the Storefront Product Pages
Build the Storefront Product Pages
Connect the V0-generated storefront components to your PrestaShop API routes. Create the product listing page using React Server Components for initial data fetching (giving you fast server-rendered page loads) and client components for interactive features like cart state and variant selection. For the product grid, use `generateStaticParams` in Next.js to pre-render category pages at build time — fetch the category list and return each category ID as a static param. This gives fast initial page loads from Vercel's CDN without a server request for the most common browsing patterns. For product images, PrestaShop serves images directly from its Web Services API at `{PS_URL}/api/images/products/{product_id}/{image_id}?ws_key={PS_KEY}`. Since the API key is in the URL (not in a header), these image URLs are technically exposed to the client. For production, consider setting up image proxying through your Next.js API route to keep the key server-side, or use a read-only API key with image-only access. For the checkout flow, redirect users to your PrestaShop store's native cart page after adding items. Alternatively, build cart state in your Next.js app and use PrestaShop's API to create a cart (`POST /api/carts`) and then redirect to PrestaShop's checkout. The fully headless checkout (handling payment entirely in your Next.js app) requires additional complexity: integrating a payment provider and using PrestaShop's order creation API. For product search, PrestaShop's API supports search with `filter[name]=%[search_term]%` but this is case-sensitive. For better search, consider fetching all products and implementing client-side fuzzy search with a library like Fuse.js, or use PrestaShop's search module if installed.
Connect the product grid to /api/prestashop?resource=products. Map each product to a card showing the product name, price formatted as currency, and a product image using the imageUrl. When a card is clicked, navigate to /products/[id]. Create the product detail page that fetches /api/prestashop?resource=product&id=[id] and shows the full product description, price, and an 'Order Now' button that links to the PrestaShop product page for checkout. Add category filter buttons that re-fetch products with ?categoryId=X.
Paste this in V0 chat
1// Example: Product listing page as React Server Component2// app/products/page.tsx3import Image from 'next/image';45interface Product {6 id: number;7 name: string;8 shortDescription: string;9 price: string;10 imageUrl: string;11}1213async function getProducts(categoryId?: string): Promise<Product[]> {14 const params = new URLSearchParams({ resource: 'products' });15 if (categoryId) params.set('categoryId', categoryId);16 const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000';17 const res = await fetch(`${baseUrl}/api/prestashop?${params}`, {18 next: { revalidate: 300 },19 });20 const data = await res.json();21 return data.products || [];22}2324export default async function ProductsPage({25 searchParams,26}: {27 searchParams: { category?: string };28}) {29 const products = await getProducts(searchParams.category);3031 return (32 <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 p-6">33 {products.map((product) => (34 <a key={product.id} href={`/products/${product.id}`}35 className="group border rounded-xl overflow-hidden hover:shadow-lg transition-shadow">36 <div className="aspect-square relative bg-gray-100">37 <img38 src={product.imageUrl}39 alt={product.name}40 className="object-cover w-full h-full group-hover:scale-105 transition-transform"41 />42 </div>43 <div className="p-4">44 <h3 className="font-semibold text-gray-900">{product.name}</h3>45 <p className="text-sm text-gray-500 mt-1 line-clamp-2">{product.shortDescription}</p>46 <p className="text-lg font-bold text-gray-900 mt-2">${product.price}</p>47 </div>48 </a>49 ))}50 </div>51 );52}Pro tip: PrestaShop's pagination uses offset-based format: `limit=12,0` for the first page (12 items starting at offset 0), `limit=12,12` for the second page. This is different from the more common `page=1&per_page=12` format.
Expected result: The product grid shows real PrestaShop products with images, names, and prices. Category filtering works. Clicking a product navigates to the detail page with full product information.
Common use cases
Modern Product Catalog Frontend
Replace PrestaShop's template with a fast, modern product catalog built with V0. The storefront shows products in a filterable grid, with category navigation, search, and product detail pages — all powered by PrestaShop's product database via API routes.
Create an e-commerce product catalog page with a sidebar for category filtering and price range slider, and a main product grid (3 columns) showing: product image, product name, price in bold, original price crossed out if on sale, stock badge ('In Stock' / 'Low Stock' / 'Out of Stock'), and an 'Add to Cart' button. Include a search bar at the top. Show 12 products with pagination.
Copy this prompt to try it in V0
Product Detail Page with Variants
Build a product detail page that shows all product variants (colors, sizes) from PrestaShop, updates the price and availability based on the selected combination, and links to PrestaShop's cart for checkout.
Create a product detail page with: large product image gallery with thumbnail strip, product name as h1, current price and sale price, star rating (4.5 stars), variant selector (color swatches and size buttons), quantity selector, 'Add to Cart' button, product description tabs (Description, Specifications, Reviews), and related products row at the bottom.
Copy this prompt to try it in V0
B2B Trade Portal
Build a wholesale portal where registered B2B customers log in to see their negotiated prices, place bulk orders, and track order history — all pulling from PrestaShop's B2B pricing groups and customer accounts via the API.
Create a B2B product order form with a searchable product table showing: product reference, name, your price (B2B rate), minimum order quantity, current stock, quantity input field, and 'Add to Order' button. Include an order summary panel on the right showing running total. Add category tabs at the top for quick filtering.
Copy this prompt to try it in V0
Troubleshooting
API returns 404 with 'This method combination is not allowed'
Cause: The Web Services API is not enabled in PrestaShop, or the API key doesn't have permission for the requested resource.
Solution: Go to PrestaShop admin → Advanced Parameters → Webservice and verify the service is enabled. Check that your API key has GET permission for the resource you're requesting. For images, you need GET permission on the 'images' resource specifically.
Product names appear as '[object Object]' or show raw JSON instead of a string
Cause: PrestaShop stores multilingual content as an array of objects. You're rendering the raw array instead of extracting the display string for your language.
Solution: Use the extractLangValue helper function to extract the string value for your target language ID. PrestaShop returns multilingual fields as arrays even for single-language stores.
1// Correct: extract the string value2const name = extractLangValue(product.name); // 'Blue Widget'3// Wrong: renders the raw array4const name = product.name; // [{language: {id: '1'}, value: 'Blue Widget'}]Product images return 401 Unauthorized in the browser
Cause: PrestaShop image API URLs require the API key as a query parameter, and the browser is not including it. If you're trying to use the image URL in a Next.js Image component, the key needs to be in the URL.
Solution: Include the API key in image URLs as a query parameter: `${PS_URL}/api/images/products/${id}/1?ws_key=${PS_KEY}`. For production, create an image proxy route that fetches the image server-side and forwards it to the browser, keeping the API key server-side.
1// Image proxy route: app/api/prestashop/image/route.ts2export async function GET(request: NextRequest) {3 const { searchParams } = new URL(request.url);4 const productId = searchParams.get('productId');5 const imageId = searchParams.get('imageId') || '1';6 const imageUrl = `${process.env.PRESTASHOP_URL}/api/images/products/${productId}/${imageId}?ws_key=${process.env.PRESTASHOP_API_KEY}`;7 const res = await fetch(imageUrl);8 return new Response(res.body, { headers: { 'Content-Type': res.headers.get('Content-Type') || 'image/jpeg' } });9}API returns empty array for products despite products existing in PrestaShop
Cause: The `filter[active]=1` filter or the category filter may be excluding all products, or the API key doesn't have permission to view the products resource.
Solution: Remove the active filter first to see all products regardless of status. Check that products are published (active) in PrestaShop admin. Verify the API key's permissions include GET for the products resource.
1// Debug: fetch all products without filters2const data = await prestashopFetch('/products', {3 display: '[id,name,active]',4 // Remove filter[active] to see all products including inactive5});Best practices
- Create a read-only API key for the storefront with only the permissions needed — don't use an all-access key in client-facing API routes
- Cache PrestaShop API responses for at least 5 minutes — product data rarely changes minute to minute and caching dramatically reduces API calls
- Proxy PrestaShop product images through a Next.js API route to avoid exposing the API key in image URLs
- Use the display parameter to request only the fields your frontend needs — PrestaShop product objects have 100+ fields and fetching all of them wastes bandwidth
- Handle PrestaShop's multilingual array format for all text fields — always use an extractLangValue helper function rather than accessing raw array data
- Pre-render category and product pages with generateStaticParams and Next.js ISR for fast CDN-cached page loads without server requests on every visit
- Test your integration against PrestaShop's staging environment before pointing it at production — avoid testing API calls against live inventory
Alternatives
Shift4Shop is a US-hosted SaaS e-commerce platform — choose Shift4Shop if you prefer a managed cloud platform rather than self-hosting PrestaShop.
Etsy API gives access to handmade and vintage marketplace listings — choose Etsy if your products are sold on Etsy's marketplace rather than your own PrestaShop store.
Spocket provides a dropshipping supplier catalog — choose Spocket if you're building a dropshipping storefront rather than selling your own inventory managed in PrestaShop.
Frequently asked questions
Does PrestaShop's Web Services API work with all PrestaShop versions?
The Web Services API has been available since PrestaShop 1.4, but there are differences between versions. PrestaShop 1.7 and 8.x have more complete API coverage and JSON support. If you're on PrestaShop 1.6, the API returns XML by default and some endpoints work differently. The code in this guide targets PrestaShop 1.7+ with JSON output. For PrestaShop 8.x, there's also a newer API available alongside the legacy Web Services API.
Can I handle the full checkout process in my V0 Next.js app?
Yes, but it's complex. You'd need to use PrestaShop's cart API to create and manage cart items, then create an order via the API and handle payment separately with Stripe or another payment provider. For most headless implementations, the simpler approach is managing the cart in your Next.js app's state and redirecting users to PrestaShop's native checkout page when they're ready to pay. This keeps PrestaShop handling tax calculation, shipping rates, and payment — which are complex to replicate.
How do I show product variants (colors, sizes) from PrestaShop?
PrestaShop product combinations are available through the `combinations` resource. Each combination represents a specific variant (e.g., Blue + Large). Fetch combinations with `GET /api/combinations?filter[id_product]={product_id}&display=full&output_format=JSON`. Each combination has `price` (differential price modifier), `quantity`, and references to product attributes. The attributes themselves are in the `product_option_values` resource.
Is the PrestaShop Web Services API rate limited?
PrestaShop's Web Services API doesn't have built-in rate limiting by default — it's constrained by your server's capacity. For self-hosted PrestaShop, the limiting factor is your hosting server's MySQL query performance and PHP processing speed. If you're hitting performance issues, implement aggressive Next.js fetch caching and consider using PrestaShop's built-in cache (APCu or Memcache) on the PHP side to speed up API responses.
Can I use this integration with PrestaShop Cloud?
Yes, PrestaShop Cloud (the hosted SaaS version) includes the Web Services API. Enable it in your PrestaShop Cloud admin panel under Advanced Parameters → Webservice. The API endpoint and authentication work identically to self-hosted PrestaShop. The main difference is you don't control the server, so you cannot install additional modules that might affect API behavior.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation