Back to Top

How to create Product page using Shopware API in NextJs

Updated 8 November 2023

How to use Next.JS with Shopware API   Hello Guys, Today in this blog we will see all about the API integration of the Shopware product detail page with the Next JS.

Shopware 6 is an open-source, content management system (CMS) that helps online merchants manage their inventory and orders. It’s based on Symfony Framework and Vue and is supported by a worldwide community.

First, we’ll have some discussion about NextJS and begin by introducing the concept of Shopware and its API.

What is NextJS?

Next Js is a full-stack web development react framework. Which is a free and open-source front-end react library framework for building user interfaces based on UI components.

All the features needed to create a variety of websites and apps are provided by NextJs, including routing, static and server rendering, static-side regeneration, and support for Typescript.

Searching for an experienced
Shopware Company ?
Find out More

For CSS design if you want to make a good user interface you can choose your desired Dependencies for the design part.

About Shopware API

The Shopware API allows developers to interact with and integrate Shopware with other systems and applications. It provides a set of services that enable developers to perform various operations, such as managing products, customers, orders, and shopping carts.

Starting Out

Setting up the project – The first step is to set up this project. Check out this Setup NextJS Project blog that explains everything about setting up a NextJS project. Also, we are using Tailwind CSS within the Project.

Global Variable Setup – We’re going to create a file named .env in the root directory and add the following code.

//------------.env--------------//
SHOPWARE_ENDPOINT=<SHOPWARE_ENDPOINT>
APP_URL=https://myshopware.com/
SW_ACCESS_KEY=<SW_ACCESS_KEY>
IMAGE_DOMAIN=<myshopware.com>

You also need to define environment variables in your next.config.js file. If you don’t define them, you won’t be able to use these variables in the front end.

/** @type {import('next').NextConfig} */
const nextConfig = {
  env: {
    SW_ACCESS_KEY: process.env.SW_ACCESS_KEY,
    SHOPWARE_ENDPOINT: process.env.SHOPWARE_ENDPOINT,
    IMAGE_DOMAIN: process.env.IMAGE_DOMAIN,
  },
  images: {
    domains: [process.env.IMAGE_DOMAIN],
  },
  reactStrictMode: true,
}

module.exports = nextConfig

Make sure you have the required API endpoint URLs (product, authenticate, and other parameters). To fetch data from the Shopware API, you need to use the Fetch function or a library such as Axios.

API Response (The mentioned image demonstrates Shopware’s product detail page’s API response):

Screenshot-from-2023-11-08-13-38-44

Create a Route file to Display the Product Detail Page:

The following code will be added to a file called [urlKey].js that will be created in pages/product/[urlKey].js.

//---------------pages/product/[urlkey].js-----------//

import Image from "next/image";
import React from "react";

const Product = ({ product }) => {
  const { cover, calculatedPrice, translated, availableStock } = product;

  return (
    <React.Fragment>
      <div className="mt-6">
        <div className="lg:px-24 md:px-12 px-3">
          <div className="container mx-auto px-4">
            <div className="lg:col-gap-12 xl:col-gap-16 mt-3 grid grid-cols-1 gap-12 lg:mt-12 lg:grid-cols-5 lg:gap-16">
              <div className="lg:col-span-3 lg:row-end-1">
                <div className="lg:order-2 ">
                  <div className="max-w-xl overflow-hidden rounded-lg">
                    <Image
                      className="h-[70vh] mix-blend-darken"
                      src={cover?.media?.url}
                      width={500}
                      height={230}
                      priority={true}
                      alt="loading"
                    />
                  </div>
                </div>
              </div>
              <div className="lg:col-span-4 lg:row-span-2 lg:row-end-2">
                <h1 className="sm: text-2xl font-bold text-gray-900 sm:text-3xl">
                  {translated.name}
                </h1>
                <div className=" mt-3">
                  <p className=" text-sm font-medium text-gray-500">
                    In Stock : {availableStock}
                  </p>
                </div>
                <h2 className="mt-3 text-base text-gray-900">Description: </h2>
                <div className="mt-3  flex select-none flex-wrap items-center gap-1">
                  <section>
                    <div
                      dangerouslySetInnerHTML={{
                        __html: translated.description.toString(),
                      }}
                    ></div>
                  </section>
                </div>
                <div className="mt-10 flex flex-col items-center justify-between space-y-4 border-t border-b py-4 sm:flex-row sm:space-y-0">
                  <div className="flex items-end">
                    <h1 className="text-3xl font-bold">
                      ${calculatedPrice.totalPrice}
                    </h1>
                  </div>
                  <button
                    type="button"
                    className="inline-flex items-center justify-center rounded-md border-2 border-transparent bg-gray-900 bg-none px-12 py-3 text-center text-base font-bold text-white transition-all duration-200 ease-in-out focus:shadow hover:bg-gray-800"
                  >
                    <svg
                      xmlns="http://www.w3.org/2000/svg"
                      className="shrink-0 mr-3 h-5 w-5"
                      fill="none"
                      viewBox="0 0 24 24"
                      stroke="currentColor"
                      strokeWidth={2}
                    >
                      <path
                        strokeLinecap="round"
                        strokeLinejoin="round"
                        d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"
                      />
                    </svg>
                    Add to cart
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </React.Fragment>
  );
};

export default Product;

export async function getStaticPaths() {
  let paths = [];
  try {
    const data = await fetchProduct();
    const products = data?.products?.items || [];
    paths = products.map((product) => ({
      params: { urlKey: product?.url_key || "404" },
    }));
  } catch (error) {
    console.error("Error fetching data from API:", error);
  }

  return { paths, fallback: "blocking" };
}

export async function getStaticProps({ params }) {
  const productInfo = await fetchProduct(params?.urlKey);
  if (productInfo.length > 0) {
    return {
      notFound: true,
    };
  }
  return {
    props: {
      product: productInfo?.product,
    },
    revalidate: 100,
  };
}

const fetchProduct = async (urlKey = null) => {
  if (urlKey) {
    const URL = `${process.env.SHOPWARE_ENDPOINT}/product/${urlKey}`;
    const response = await fetch(URL, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
        "sw-access-key": process.env.SW_ACCESS_KEY,
      },
      body: undefined,
    });
    if (response.status !== 200) {
      throw new Error(`API request failed with status ${response.status}`);
    }
    return response.json();
  }
};

You can see the result on http://localhost:3000

Screenshot-from-2023-11-08-16-38-23

Start Shopware Headless Development with Webkul.

Happy Coding 🙂

. . .

Leave a Comment

Your email address will not be published. Required fields are marked*


Be the first to comment.

Back to Top

Message Sent!

If you have more details or questions, you can reply to the received confirmation email.

Back to Home