{"id":28364372,"url":"https://github.com/pramit-marattha/supabaseecommerce","last_synced_at":"2025-06-23T15:32:46.145Z","repository":{"id":42712187,"uuid":"464768596","full_name":"pramit-marattha/SupabaseEcommerce","owner":"pramit-marattha","description":"Supabase Ecommerce","archived":false,"fork":false,"pushed_at":"2023-02-24T08:27:49.000Z","size":67470,"stargazers_count":30,"open_issues_count":1,"forks_count":8,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-04T23:09:55.469Z","etag":null,"topics":["next","nextjs","react","reactjs","tutorial"],"latest_commit_sha":null,"homepage":"https://supabase-ecommerce.vercel.app","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pramit-marattha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-03-01T06:18:17.000Z","updated_at":"2025-05-30T05:12:00.000Z","dependencies_parsed_at":"2022-09-09T16:01:16.325Z","dependency_job_id":null,"html_url":"https://github.com/pramit-marattha/SupabaseEcommerce","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pramit-marattha/SupabaseEcommerce","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pramit-marattha%2FSupabaseEcommerce","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pramit-marattha%2FSupabaseEcommerce/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pramit-marattha%2FSupabaseEcommerce/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pramit-marattha%2FSupabaseEcommerce/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pramit-marattha","download_url":"https://codeload.github.com/pramit-marattha/SupabaseEcommerce/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pramit-marattha%2FSupabaseEcommerce/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261504519,"owners_count":23168852,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["next","nextjs","react","reactjs","tutorial"],"created_at":"2025-05-28T20:44:03.766Z","updated_at":"2025-06-23T15:32:46.126Z","avatar_url":"https://github.com/pramit-marattha.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align =\"center\"\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/37651620/221128591-9de903e4-7ac3-48d8-805f-4ac2b3a83262.png\" /\u003e\n\u003c/p\u003e\n\n\nWe will be building `SuperbaseEcommerce` full-stack application in this tutorial. This application is simply an online e-commerce shopping site where users can browse all of the products, upload their own products, and even purchase the products (`this functionality will be added in the next series`). It is similar to an Amazon app, but it is simpler because we will not implement any actual payment or shipping procedures. Here's a live demonstration of the final version of the app. This is how your app should look after you finish this tutorial. Feel free to experiment with it to get a sense of all the features we will be implementing.\n\n### Live Demo =\u003e [https://supabase-ecommerce.vercel.app](https://supabase-ecommerce.vercel.app)\n\n![Demo](https://user-images.githubusercontent.com/37651620/159170940-d8c210c4-5b9d-47f7-b5d8-547bea3106fc.png)\n\n![Demo](https://user-images.githubusercontent.com/37651620/159170944-b48ead6f-6482-4234-a5ea-7b20335ca7fe.png)\n\nSo, in this tutorial, we'll learn how to build this full-stack app with `Next.js`, the react framework, `NextAuth.js`, for implementing passwordless and OAuth authentication, `Supabase`, for persisting app data into a PostgreSQL database and stashing media files and information, and `Prisma`, for making it simple to read and write data from and to the database from our app.\n\nThis tutorial covers many topics and technical concepts necessary to build a modern full-stack app, even if this app is a simplified version of a more advanced ecommerce site like Amazon. You should be able to use all of the technologies covered in this tutorial, including react, nextjs, prisma, supabase, and others, but most importantly, you should be able to build any full-stack app using those technologies. You'll go at your own speed and intensity, with us guiding you along the way. After completing this guide, the goal of this tutorial is to provide you with the tools and techniques you'll need to build a similar app on your own.To put it another way, this tutorial will not only teach you how to use those technologies in great detail, but it will also provide you with the proper mixture of principles and application to help you grasp all of the key concepts so that you can proudly build your own apps from scratch later part on this tutorial.\n\nLet's start with the react portion and build our application. The first step is to install Node.js if it isn't already on your computer. So, go to the official Node.js website and download the most recent version. Node js is required to use the node package manager, abbreviated as npm. Now launch your preferred code editor and navigate to the folder. For this tutorial, we'll be using the VScode code editor.\n\n### Setting up SupabaseEcommerce project.\n\nThere is a [Github repository](https://github.com/pramit-marattha/SupabaseEcommerce) dedicated to this project, which consists of three branches. Clone the [`SupabaseEcommerce-starter`](https://github.com/pramit-marattha/SupabaseEcommerce/tree/SupabaseEcommerce-starter) branch to get started.\n\n![Github](https://user-images.githubusercontent.com/37651620/158067824-de726446-b049-4ebb-9c2f-adbecd571d64.png)\n\nThe `Main` branch contains the entire `final` source code of the application, so clone the `SupabaseEcommerce-starter` branch if you want to follow along with this tutorial.\n\n```\ngit clone --branch SupabaseEcommerce-starter https://github.com/pramit-marattha/SupabaseEcommerce.git\n```\n\nAfter that, head over to the cloned directory and install the dependencies before starting the `Next.js` development server:\n\n```bash\ncd SupabaseEcommerce\nyarn add all\nyarn dev\n```\n\nYou can now check if everything is working properly by going to `http://localhost:3000` and editing `pages/index.js`, then viewing the updated result in your browser.For more information on how to use `create-next-app`, you can review the [create-next-app documentation](https://nextjs.org/docs/api-reference/create-next-app).\n\n![Documentation](https://user-images.githubusercontent.com/37651620/158068425-c3087d4a-14fb-46ac-a99c-10cd6412624f.png)\n\nIt usually only takes a few minutes to get everything set up. So, for this project we will be using `yarn` to add packages to a project, which will install and configure everything for us so that we can get started right away with an excellent starter template. It's time to start our development server, so head over to that `SupabaseEcommerce` folder and type `yarn add all` and then `yarn dev` and the browser will instantly open our starter template `Next.js` appplication.\n\n![Demo](https://user-images.githubusercontent.com/37651620/158068726-0b8eafe8-6c4d-45eb-98cf-ef64faf58103.png)\n\nYour application’s folder structure should look something like this.\n\n![Folder structure](https://user-images.githubusercontent.com/37651620/160289553-47f4b441-d15b-4f20-85de-6e255035444a.png)\n\nSo you might be curious about the source of the content. Remember that all of our source code is housed in the pages folder, and react/next will inject it into the root div element. Let’s take a look at our pages folder, which contains some javascript files and one API folder.\n\n![Pages](https://user-images.githubusercontent.com/37651620/158071549-8daff075-bf73-4873-96ca-ab3cd473848d.png)\n\nBefore we dive any further lets actually create a landing page for our site.\n\nso before we even begin first you need to install `framer-motion` library.\n\n![Framer Motion](https://user-images.githubusercontent.com/37651620/158306049-86478da8-4e06-473d-bfec-493196a228de.png)\n\nLet's dive in and create a beautiful looking UI for our E-commerce application before we start on the backend integration part. Let's start by making a landing page for the app, and then move on to making a product page for it. So, inside the `components` folder, create a `Layout` component and add the following code to it. This component is simply a basic layout for our application that includes a navigation bar \u0026 menus as well as the functionality to display our application's registration/login modal.\n\n```jsx\n// components/Layout.js\nimport { Fragment, useState } from \"react\";\nimport { useRouter } from \"next/router\";\nimport Head from \"next/head\";\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport PropTypes from \"prop-types\";\nimport SigninPopupModal from \"./SigninPopupModal\";\nimport { Menu, Transition } from \"@headlessui/react\";\nimport {\n  HeartIcon,\n  HomeIcon,\n  LogoutIcon,\n  PlusIcon,\n  UserIcon,\n  ShoppingCartIcon,\n} from \"@heroicons/react/outline\";\nimport { ChevronDownIcon } from \"@heroicons/react/solid\";\n\nconst menuItems = [\n  {\n    label: \"List a new home\",\n    icon: PlusIcon,\n    href: \"/list\",\n  },\n  {\n    label: \"My homes\",\n    icon: HomeIcon,\n    href: \"/homes\",\n  },\n  {\n    label: \"Favorites\",\n    icon: HeartIcon,\n    href: \"/favorites\",\n  },\n  {\n    label: \"Logout\",\n    icon: LogoutIcon,\n    onClick: () =\u003e null,\n  },\n];\n\nconst Layout = ({ children = null }) =\u003e {\n  const router = useRouter();\n\n  const [showModal, setShowModal] = useState(false);\n\n  const user = null;\n  const isLoadingUser = false;\n\n  const openModal = () =\u003e setShowModal(true);\n  const closeModal = () =\u003e setShowModal(false);\n\n  return (\n    \u003c\u003e\n      \u003cHead\u003e\n        \u003ctitle\u003eSupaaShop | A new way to shop!\u003c/title\u003e\n        \u003cmeta name=\"title\" content=\"SupaaShopp\" /\u003e\n        \u003clink rel=\"icon\" href=\"/favicon.ico\" /\u003e\n      \u003c/Head\u003e\n\n      \u003cdiv className=\"min-h-screen flex flex-col font-['Poppins'] bg-[linear-gradient(90deg, #161122 21px, transparent 1%) center, linear-gradient(#161122 21px, transparent 1%) center, #a799cc]\"\u003e\n        \u003cheader className=\"h-28 w-full shadow-lg\"\u003e\n          \u003cdiv className=\"h-full container mx-auto\"\u003e\n            \u003cdiv className=\"h-full px-5 flex justify-between items-center space-x-5\"\u003e\n              \u003cLink href=\"/\"\u003e\n                \u003ca className=\"flex items-center space-x-1\"\u003e\n                  \u003cimg\n                    className=\"shrink-0 w-24 h-24 text-primary\"\n                    src=\"https://user-images.githubusercontent.com/37651620/158058874-6a86646c-c60e-4c39-bc6a-d81974afe635.png\"\n                    alt=\"Logo\"\n                  /\u003e\n                  \u003cspan className=\"text-2xl font-semibold tracking-wide text-white\"\u003e\n                    \u003cspan className=\"text-3xl text-success\"\u003eS\u003c/span\u003eupabase\n                    \u003cspan className=\"text-3xl text-success\"\u003eE\u003c/span\u003ecommerce\n                  \u003c/span\u003e\n                \u003c/a\u003e\n              \u003c/Link\u003e\n              \u003cdiv className=\"flex items-center space-x-4\"\u003e\n                \u003cLink href=\"/create\"\u003e\n                  \u003ca className=\"ml-4 px-4 py-5 rounded-md bg-info text-primary hover:bg-primary hover:text-info focus:outline-none focus:ring-4 focus:ring-primaryfocus:ring-opacity-50  font-semibold transition\"\u003e\n                    Register shop !\n                  \u003c/a\u003e\n                \u003c/Link\u003e\n                {isLoadingUser ? (\n                  \u003cdiv className=\"h-8 w-[75px] bg-gray-200 animate-pulse rounded-md\" /\u003e\n                ) : user ? (\n                  \u003cMenu as=\"div\" className=\"relative z-50\"\u003e\n                    \u003cMenu.Button className=\"flex items-center space-x-px group\"\u003e\n                      \u003cdiv className=\"shrink-0 flex items-center justify-center rounded-full overflow-hidden relative bg-gray-200 w-9 h-9\"\u003e\n                        {user?.image ? (\n                          \u003cImage\n                            src={user?.image}\n                            alt={user?.name || \"Avatar\"}\n                            layout=\"fill\"\n                          /\u003e\n                        ) : (\n                          \u003cUserIcon className=\"text-gray-400 w-6 h-6\" /\u003e\n                        )}\n                      \u003c/div\u003e\n                      \u003cChevronDownIcon className=\"w-5 h-5 shrink-0 text-gray-500 group-hover:text-current\" /\u003e\n                    \u003c/Menu.Button\u003e\n                    \u003cTransition\n                      as={Fragment}\n                      enter=\"transition ease-out duration-100\"\n                      enterFrom=\"opacity-0 scale-95\"\n                      enterTo=\"opacity-100 scale-100\"\n                      leave=\"transition ease-in duration-75\"\n                      leaveFrom=\"opacity-100 scale-100\"\n                      leaveTo=\"opacity-0 scale-95\"\n                    \u003e\n                      \u003cMenu.Items className=\"absolute right-0 w-72 overflow-hidden mt-1 divide-y divide-gray-100 origin-top-right bg-white rounded-md shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none\"\u003e\n                        \u003cdiv className=\"flex items-center space-x-2 py-4 px-4 mb-2\"\u003e\n                          \u003cdiv className=\"shrink-0 flex items-center justify-center rounded-full overflow-hidden relative bg-gray-200 w-9 h-9\"\u003e\n                            {user?.image ? (\n                              \u003cImage\n                                src={user?.image}\n                                alt={user?.name || \"Avatar\"}\n                                layout=\"fill\"\n                              /\u003e\n                            ) : (\n                              \u003cUserIcon className=\"text-gray-400 w-6 h-6\" /\u003e\n                            )}\n                          \u003c/div\u003e\n                          \u003cdiv className=\"flex flex-col truncate\"\u003e\n                            \u003cspan\u003e{user?.name}\u003c/span\u003e\n                            \u003cspan className=\"text-sm text-gray-500\"\u003e\n                              {user?.email}\n                            \u003c/span\u003e\n                          \u003c/div\u003e\n                        \u003c/div\u003e\n                        \u003cdiv className=\"py-2\"\u003e\n                          {menuItems.map(\n                            ({ label, href, onClick, icon: Icon }) =\u003e (\n                              \u003cdiv\n                                key={label}\n                                className=\"px-2 last:border-t last:pt-2 last:mt-2\"\n                              \u003e\n                                \u003cMenu.Item\u003e\n                                  {href ? (\n                                    \u003cLink href={href}\u003e\n                                      \u003ca className=\"flex items-center space-x-2 py-2 px-4 rounded-md hover:bg-gray-100\"\u003e\n                                        \u003cIcon className=\"w-5 h-5 shrink-0 text-gray-500\" /\u003e\n                                        \u003cspan\u003e{label}\u003c/span\u003e\n                                      \u003c/a\u003e\n                                    \u003c/Link\u003e\n                                  ) : (\n                                    \u003cbutton\n                                      className=\"w-full flex items-center space-x-2 py-2 px-4 rounded-md hover:bg-gray-100\"\n                                      onClick={onClick}\n                                    \u003e\n                                      \u003cIcon className=\"w-5 h-5 shrink-0 text-gray-500\" /\u003e\n                                      \u003cspan\u003e{label}\u003c/span\u003e\n                                    \u003c/button\u003e\n                                  )}\n                                \u003c/Menu.Item\u003e\n                              \u003c/div\u003e\n                            )\n                          )}\n                        \u003c/div\u003e\n                      \u003c/Menu.Items\u003e\n                    \u003c/Transition\u003e\n                  \u003c/Menu\u003e\n                ) : (\n                  \u003cbutton\n                    type=\"button\"\n                    onClick={openModal}\n                    className=\"ml-4 px-4 py-5 rounded-md bg-info hover:bg-primary focus:outline-none focus:ring-4 focus:ring-primary focus:ring-opacity-50 text-primary hover:text-info font-extrabold transition\"\n                  \u003e\n                    Login\n                  \u003c/button\u003e\n                )}\n              \u003c/div\u003e\n            \u003c/div\u003e\n          \u003c/div\u003e\n        \u003c/header\u003e\n\n        \u003cmain className=\"flex-grow container mx-auto\"\u003e\n          \u003cdiv className=\"px-4 py-12\"\u003e\n            {typeof children === \"function\" ? children(openModal) : children}\n          \u003c/div\u003e\n        \u003c/main\u003e\n\n        \u003cSigninPopupModal show={showModal} onClose={closeModal} /\u003e\n      \u003c/div\u003e\n    \u003c/\u003e\n  );\n};\n\nLayout.propTypes = {\n  children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),\n};\n\nexport default Layout;\n```\n\nLet's create a 'Hero' section of our landing page after you've successfully created a layout for the application. To do so, simply paste the following code into that section. So, in this section, we'll add an image on the right, a large text heading, and two buttons on the left.Note that we are styling our project with the absolute power of `tailwind css` and `framer-motion` to add some beautiful transition animation to the image .Since we've already created buttons on our starter template, you won't have to worry about creating them from scratch; instead, you can simply import them from the components and use them.\n\n```jsx\n// components/Hero.js\nimport React from \"react\";\nimport PrimaryButton from \"@/components/PrimaryButton\";\nimport SecondaryButton from \"@/components/SecondaryButton\";\nimport { motion } from \"framer-motion\";\n\nconst Hero = () =\u003e {\n  return (\n    \u003cdiv className=\"max-w-6xl mx-auto py-12 flex flex-col md:flex-row space-y-8 md:space-y-0\"\u003e\n      \u003cdiv className=\"w-full md:w-1/2 flex flex-col justify-center items-center\"\u003e\n        \u003cdiv className=\"max-w-xs lg:max-w-md space-y-10 w-5/6 mx-auto md:w-full text-center md:text-left\"\u003e\n          \u003ch1 className=\"font-primary font-extrabold text-white text-3xl sm:text-4xl md:text-5xl md:leading-tight\"\u003e\n            Shop \u003cspan className=\"text-success\"\u003ewhenever\u003c/span\u003e and{\" \"}\n            \u003cspan className=\"text-success\"\u003ehowever\u003c/span\u003e you want from,{\" \"}\n            \u003cspan className=\"text-success\"\u003ewherever\u003c/span\u003e you are..{\" \"}\n          \u003c/h1\u003e\n          \u003cp className=\"font-secondary text-gray-500 text-base md:text-lg lg:text-xl\"\u003e\n            SuperbaseEcommerce improves and streamlines your shopping\n            experience..\n          \u003c/p\u003e\n          \u003cdiv className=\"flex space-x-4\"\u003e\n            \u003cPrimaryButton text=\"Register\" link=\"/\" /\u003e\n            \u003cSecondaryButton text=\"Let's Shop!\" link=\"/products\" /\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cmotion.div\n        className=\"w-full md:w-1/2 transform scale-x-125 lg:scale-x-100\"\n        initial={{ opacity: 0, translateY: 60 }}\n        animate={{ opacity: 1, translateY: 0 }}\n        transition={{ duration: 0.8, translateY: 0 }}\n      \u003e\n        \u003cimg\n          alt=\"hero-img\"\n          src=\"./assets/shop.svg\"\n          className=\"mx-auto object-cover shadow rounded-tr-extraLarge rounded-bl-extraLarge w-full h-96 sm:h-112 md:h-120\"\n        /\u003e\n      \u003c/motion.div\u003e\n    \u003c/div\u003e\n  );\n};\n\nexport default Hero;\n```\n\nNow, before re-running the server, import this `Hero` component into the `index.js` file and wrap it in the Layout component to see the changes you've made.\n\n```jsx\n// index.js\nimport Layout from \"@/components/Layout\";\nimport Hero from \"@/components/Hero\";\n\nexport default function Home() {\n  return (\n    \u003cLayout\u003e\n      \u003cHero /\u003e\n    \u003c/Layout\u003e\n  );\n}\n```\n\nThis is how your landing page should appear.\n\n![Demo](https://user-images.githubusercontent.com/37651620/159169803-0d0ed5ff-0084-45d8-b2b9-f802d4e69d2b.png)\n\nAfter you've finished with the `Hero` section, go ahead and create a `ShopCards` component, where we'll simply list the demo features that this application offers and add some images, so your final code for the `ShopCards` component should look like this.\n\n```jsx\n// components/ShopCards.js\nimport React, { useState, useEffect, useRef } from \"react\";\nimport { motion } from \"framer-motion\";\n\nconst ShopCards = () =\u003e {\n  const [tab, setTab] = useState(1);\n\n  const tabs = useRef(null);\n\n  const heightFix = () =\u003e {\n    if (tabs.current.children[tab]) {\n      tabs.current.style.height =\n        tabs.current.children[tab - 1].offsetHeight + \"px\";\n    }\n  };\n\n  useEffect(() =\u003e {\n    heightFix();\n  }, [tab]);\n  return (\n    \u003csection className=\"relative\"\u003e\n      \u003cdiv\n        className=\"absolute inset-0 pointer-events-none pb-26\"\n        aria-hidden=\"true\"\n      \u003e\u003c/div\u003e\n\n      \u003cdiv className=\"relative max-w-6xl mx-auto px-4 sm:px-6\"\u003e\n        \u003cdiv className=\"pt-12 md:pt-20\"\u003e\n          \u003cdiv className=\"max-w-3xl mx-auto text-center pb-12 md:pb-16\"\u003e\n            \u003ch1 className=\"text-3xl mb-4\"\u003eFeatures\u003c/h1\u003e\n            \u003cp className=\"text-xl text-gray-500\"\u003e\n              List of features that SuperbaseEcommerce provides.\n            \u003c/p\u003e\n          \u003c/div\u003e\n          \u003cdiv className=\"relative max-w-6xl mx-auto px-4 sm:px-6\"\u003e\n            \u003cdiv className=\"pt-12 md:pt-20\"\u003e\n              \u003cdiv className=\"max-w-3xl mx-auto text-center pb-6 md:pb-16\"\u003e\n                \u003cdiv className=\"\" data-aos=\"zoom-y-out\" ref={tabs}\u003e\n                  \u003cmotion.div\n                    className=\"relative w-full h-full\"\n                    initial={{ opacity: 0, translateY: 60 }}\n                    animate={{ opacity: 1, translateY: 0 }}\n                    transition={{ duration: 0.8, translateY: 0 }}\n                  \u003e\n                    \u003cimg\n                      alt=\"hero-img\"\n                      src=\"./assets/webShop.svg\"\n                      className=\"mx-auto object-cover shadow rounded-tr-extraLarge rounded-bl-extraLarge w-full h-96 sm:h-112 md:h-120\"\n                    /\u003e\n                  \u003c/motion.div\u003e\n                \u003c/div\u003e\n              \u003c/div\u003e\n            \u003c/div\u003e\n          \u003c/div\u003e\n\n          \u003cdiv className=\"max-w-6xl mx-auto py-12 flex flex-col md:flex-row space-y-8 md:space-y-0\"\u003e\n            \u003cdiv\n              className=\"max-w-xl md:max-w-none md:w-full mx-auto md:col-span-7 lg:col-span-6 md:mt-6 pr-12\"\n              data-aos=\"fade-right\"\n            \u003e\n              \u003cdiv className=\"md:pr-4 lg:pr-12 xl:pr-16 mb-8\"\u003e\n                \u003ch3 className=\"h3 mb-3\"\u003eAll of our awesome features\u003c/h3\u003e\n                \u003cp className=\"text-xl text-black\"\u003e\u003c/p\u003e\n              \u003c/div\u003e\n              \u003cdiv className=\"mb-8 md:mb-0\"\u003e\n                \u003ca\n                  className={`flex items-center text-lg p-5 rounded border transition duration-300 ease-in-out mb-3 ${\n                    tab !== 1\n                      ? \"bg-white shadow-md border-success hover:shadow-lg\"\n                      : \"bg-success border-transparent\"\n                  }`}\n                  href=\"#0\"\n                  onClick={(e) =\u003e {\n                    e.preventDefault();\n                    setTab(1);\n                  }}\n                \u003e\n                  \u003cdiv\u003e\n                    \u003cdiv className=\"font-bold leading-snug tracking-tight mb-1 text-gray-600\"\u003e\n                      Register/Login Feature\n                    \u003c/div\u003e\n                    \u003cdiv className=\"text-gray-600\"\u003e\n                      User can login and save their products for later purchase.\n                    \u003c/div\u003e\n                  \u003c/div\u003e\n                \u003c/a\u003e\n                \u003ca\n                  className={`flex items-center text-lg p-5 rounded border transition duration-300 ease-in-out mb-3 ${\n                    tab !== 2\n                      ? \"bg-white shadow-md border-purple-200 hover:shadow-lg\"\n                      : \"bg-success border-transparent\"\n                  }`}\n                  href=\"#0\"\n                  onClick={(e) =\u003e {\n                    e.preventDefault();\n                    setTab(2);\n                  }}\n                \u003e\n                  \u003cdiv\u003e\n                    \u003cdiv className=\"font-bold leading-snug tracking-tight mb-1 text-gray-600\"\u003e\n                      Add to cart\n                    \u003c/div\u003e\n                    \u003cdiv className=\"text-gray-600\"\u003e\n                      User can add the products/items to their cart\n                    \u003c/div\u003e\n                  \u003c/div\u003e\n                \u003c/a\u003e\n                \u003ca\n                  className={`flex items-center text-lg p-5 rounded border transition duration-300 ease-in-out mb-3 ${\n                    tab !== 3\n                      ? \"bg-white shadow-md border-purple-200 hover:shadow-lg\"\n                      : \"bg-success border-transparent\"\n                  }`}\n                  href=\"#0\"\n                  onClick={(e) =\u003e {\n                    e.preventDefault();\n                    setTab(3);\n                  }}\n                \u003e\n                  \u003cdiv\u003e\n                    \u003cdiv className=\"font-bold leading-snug tracking-tight mb-1 text-gray-600\"\u003e\n                      Security\n                    \u003c/div\u003e\n                    \u003cdiv className=\"text-gray-600\"\u003e\n                      Hassle free secure login and registration process.\n                    \u003c/div\u003e\n                  \u003c/div\u003e\n                \u003c/a\u003e\n                \u003ca\n                  className={`flex items-center text-lg p-5 rounded border transition duration-300 ease-in-out mb-3 ${\n                    tab !== 4\n                      ? \"bg-white shadow-md border-purple-200 hover:shadow-lg\"\n                      : \"bg-success border-transparent\"\n                  }`}\n                  href=\"#0\"\n                  onClick={(e) =\u003e {\n                    e.preventDefault();\n                    setTab(4);\n                  }}\n                \u003e\n                  \u003cdiv\u003e\n                    \u003cdiv className=\"font-bold leading-snug tracking-tight mb-1 text-gray-600\"\u003e\n                      Personalized shops\n                    \u003c/div\u003e\n                    \u003cdiv className=\"text-gray-600\"\u003e\n                      User can create/register their very own shop and add their\n                      own products.\n                    \u003c/div\u003e\n                  \u003c/div\u003e\n                \u003c/a\u003e\n              \u003c/div\u003e\n            \u003c/div\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/section\u003e\n  );\n};\n\nexport default ShopCards;\n```\n\nAgain, before re-running the server, import this `ShopCards` component into the `index.js` file and wrap it in the `Layout` component \u0026 below the `Hero` component to see the changes you've made.\n\n```jsx\n// index.js\nimport Layout from \"@/components/Layout\";\nimport Hero from \"@/components/Hero\";\nimport ShopCards from \"@/components/ShopCards\";\n\nexport default function Home() {\n  return (\n    \u003cLayout\u003e\n      \u003cHero /\u003e\n      \u003cShopCards /\u003e\n    \u003c/Layout\u003e\n  );\n}\n```\n\nFor the time being, this is how your landing page should appear.\n\n![Demo](https://user-images.githubusercontent.com/37651620/159170136-4c2d9b7e-dc6a-4f7d-b897-bee33ac7f338.png)\n\nFinally, let's add a Footer section, so make a `Footer` component and paste the code below into it.\n\n```jsx\n// components/Footer.js\nimport Link from \"next/link\";\n\nconst Footer = () =\u003e {\n  return (\n    \u003cfooter\u003e\n      \u003cdiv className=\"max-w-6xl mx-auto px-4 sm:px-6 pt-10\"\u003e\n        \u003cdiv className=\"sm:col-span-6 md:col-span-3 lg:col-span-3\"\u003e\n          \u003csection\u003e\n            \u003cdiv className=\"max-w-6xl mx-auto px-4 sm:px-6\"\u003e\n              \u003cdiv className=\"pb-12 md:pb-20\"\u003e\n                \u003cdiv\n                  className=\"relative bg-success rounded py-10 px-8 md:py-16 md:px-12 shadow-2xl overflow-hidden\"\n                  data-aos=\"zoom-y-out\"\n                \u003e\n                  \u003cdiv\n                    className=\"absolute right-0 bottom-0 pointer-events-none hidden lg:block\"\n                    aria-hidden=\"true\"\n                  \u003e\u003c/div\u003e\n\n                  \u003cdiv className=\"relative flex flex-col lg:flex-row justify-between items-center\"\u003e\n                    \u003cdiv className=\"text-center lg:text-left lg:max-w-xl\"\u003e\n                      \u003ch6 className=\"text-gray-600 text-3xl font-medium mb-2\"\u003e\n                        Sign-up for the early access!{\" \"}\n                      \u003c/h6\u003e\n                      \u003cp className=\"text-gray-100 text-lg mb-6\"\u003e\n                        SuperbaseEcommerce improves and streamlines your\n                        shopping experience.. !\n                      \u003c/p\u003e\n                      \u003cform className=\"w-full lg:w-auto\"\u003e\n                        \u003cdiv className=\"flex flex-col sm:flex-row justify-center max-w-xs mx-auto sm:max-w-xl lg:mx-0\"\u003e\n                          \u003cinput\n                            type=\"email\"\n                            className=\"w-full appearance-none bg-purple-100 border border-gray-700 focus:border-gray-600 rounded-sm px-4 py-3 mb-2 sm:mb-0 sm:mr-2 text-black placeholder-gray-500\"\n                            placeholder=\"Enter your email…\"\n                            aria-label=\"Enter your email…\"\n                          /\u003e\n                          \u003ca\n                            className=\"btn text-white bg-info hover:bg-success shadow\"\n                            href=\"#\"\n                          \u003e\n                            Sign-Up!\n                          \u003c/a\u003e\n                        \u003c/div\u003e\n                      \u003c/form\u003e\n                    \u003c/div\u003e\n                  \u003c/div\u003e\n                \u003c/div\u003e\n              \u003c/div\u003e\n            \u003c/div\u003e\n          \u003c/section\u003e\n        \u003c/div\u003e\n        \u003cdiv className=\"md:flex md:items-center md:justify-between py-4 md:py-8 border-t-2 border-solid\"\u003e\n          \u003cul className=\"flex mb-4 md:order-1 md:ml-4 md:mb-0\"\u003e\n            \u003cli\u003e\n              \u003cLink\n                href=\"#\"\n                className=\"flex justify-center items-center text-blue-400 hover:text-gray-900 bg-blue-100 hover:bg-white-100 rounded-full shadow transition duration-150 ease-in-out\"\n                aria-label=\"Twitter\"\n              \u003e\n                \u003csvg\n                  className=\"w-8 h-8 fill-current \"\n                  viewBox=\"0 0 32 32\"\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                \u003e\n                  \u003cpath d=\"M24 11.5c-.6.3-1.2.4-1.9.5.7-.4 1.2-1 1.4-1.8-.6.4-1.3.6-2.1.8-.6-.6-1.5-1-2.4-1-1.7 0-3.2 1.5-3.2 3.3 0 .3 0 .5.1.7-2.7-.1-5.2-1.4-6.8-3.4-.3.5-.4 1-.4 1.7 0 1.1.6 2.1 1.5 2.7-.5 0-1-.2-1.5-.4 0 1.6 1.1 2.9 2.6 3.2-.3.1-.6.1-.9.1-.2 0-.4 0-.6-.1.4 1.3 1.6 2.3 3.1 2.3-1.1.9-2.5 1.4-4.1 1.4H8c1.5.9 3.2 1.5 5 1.5 6 0 9.3-5 9.3-9.3v-.4c.7-.5 1.3-1.1 1.7-1.8z\" /\u003e\n                \u003c/svg\u003e\n              \u003c/Link\u003e\n            \u003c/li\u003e\n            \u003cli className=\"ml-4\"\u003e\n              \u003cLink\n                href=\"#\"\n                className=\"flex justify-center items-center text-white hover:text-gray-900 bg-black hover:bg-white-100 rounded-full shadow transition duration-150 ease-in-out\"\n                aria-label=\"Github\"\n              \u003e\n                \u003csvg\n                  className=\"w-8 h-8 fill-current\"\n                  viewBox=\"0 0 32 32\"\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                \u003e\n                  \u003cpath d=\"M16 8.2c-4.4 0-8 3.6-8 8 0 3.5 2.3 6.5 5.5 7.6.4.1.5-.2.5-.4V22c-2.2.5-2.7-1-2.7-1-.4-.9-.9-1.2-.9-1.2-.7-.5.1-.5.1-.5.8.1 1.2.8 1.2.8.7 1.3 1.9.9 2.3.7.1-.5.3-.9.5-1.1-1.8-.2-3.6-.9-3.6-4 0-.9.3-1.6.8-2.1-.1-.2-.4-1 .1-2.1 0 0 .7-.2 2.2.8.6-.2 1.3-.3 2-.3s1.4.1 2 .3c1.5-1 2.2-.8 2.2-.8.4 1.1.2 1.9.1 2.1.5.6.8 1.3.8 2.1 0 3.1-1.9 3.7-3.7 3.9.3.4.6.9.6 1.6v2.2c0 .2.1.5.6.4 3.2-1.1 5.5-4.1 5.5-7.6-.1-4.4-3.7-8-8.1-8z\" /\u003e\n                \u003c/svg\u003e\n              \u003c/Link\u003e\n            \u003c/li\u003e\n          \u003c/ul\u003e\n\n          \u003cdiv className=\"flex-shrink-0 mr-2\"\u003e\n            \u003cLink href=\"/\" className=\"block\" aria-label=\"SuperbaseEcommerce\"\u003e\n              \u003cimg\n                className=\"object-cover h-20 w-full\"\n                src=\"https://user-images.githubusercontent.com/37651620/159121520-fe42bbf1-a2af-4baf-bdd8-7efad8523202.png\"\n                alt=\"SupabaseEcommerce\"\n              /\u003e\n            \u003c/Link\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/footer\u003e\n  );\n};\n\nexport default Footer;\n```\n\n\u003e Note: Again, before re-running the server, import this `Footer` component into the `index.js` file and wrap it in the `Layout` component \u0026 below the `ShopCards` component to see the changes you've made.\n\n```jsx\n// index.js\nimport Layout from \"@/components/Layout\";\nimport Hero from \"@/components/Hero\";\nimport ShopCards from \"@/components/ShopCards\";\nimport Footer from \"@/components/Footer\";\n\nexport default function Home() {\n  return (\n    \u003cLayout\u003e\n      \u003cHero /\u003e\n      \u003cShopCards /\u003e\n      \u003cFooter /\u003e\n    \u003c/Layout\u003e\n  );\n}\n```\n\nSo, if you re-run the server, this is what your application should look like.\n\n![Demo Footer](https://user-images.githubusercontent.com/37651620/159170273-6b13b471-5d96-4e7e-88f4-9551edc253b6.png)\n\nThe structure of your component folders should resemble something like this.\n\n![Demo](https://user-images.githubusercontent.com/37651620/159170780-1308379d-4532-4d9f-b2d7-f069b9564eac.png)\n\nCongratulationss!! Now that you've successfully created a landing page for the application, let's move on to the core of the matter: creating the product section of the application.\n\nSo, Now let’s look at the `_app.js` file.\n\n```jsx\n// _app.js\nimport \"../styles/globals.css\";\nimport { Toaster } from \"react-hot-toast\";\n\nfunction MyApp({ Component, pageProps }) {\n  return (\n    \u003c\u003e\n      \u003cComponent {...pageProps} /\u003e\n      \u003cToaster /\u003e\n    \u003c/\u003e\n  );\n}\n\nexport default MyApp;\n```\n\nThe App component is used by `Next.js` to create pages. You can control the page initialization by simply overriding it. It allows you to do amazing things like: `Persisting layout across page changes`, `Keeping state while navigating pages`, `Custom error handling using componentDidCatch`,`Inject additional data into pages and Add global styles/CSS` are just a few of the great things you can accomplish with it.\n\nIn the above `\\_app.js` code the Component parameter represents the active page, when you switch routes, Component will change to the new page. As a result, the page will receive any props you pass to Component. Meanwhile `pageProps` is an empty object that contains the initial props that were preloaded for your page by one of the data fetching methods.\n\nNow, inside the `pages` folder, create a new page called `products.js` and import the `Layout` and `Grid` components, then import the `data.json` file as products and make the following changes to it.\n\n```jsx\n// pages/products.js\nimport Layout from \"@/components/Layout\";\nimport Grid from \"@/components/Grid\";\n\nimport products from \"data.json\";\n\nexport default function Products() {\n  return (\n    \u003cLayout\u003e\n      \u003cdiv className=\"mt-8 p-5\"\u003e\n        \u003cGrid products={products} /\u003e\n      \u003c/div\u003e\n    \u003c/Layout\u003e\n  );\n}\n```\n\n## Database Configurations\n\nBefore jumping directly on our application, we'll be utilizing the power of `Supabase` to create a `PostgreSQL` database, the `Prisma schema` to define the app data model, and Next.js to connect those two together. So, let's get started building our database.\n\n### Supabase Configurartion\n\nCreating a PostgreSQL database in Supabase is as simple as starting a new project. Head over to [supabase.com](https://supabase.com/) and `Sign-in` to your account.\n\n![Supabase](https://user-images.githubusercontent.com/37651620/159206560-3b46e8b2-ded2-4146-97e3-4a640b72c8b7.png)\n\nAfter you've successfully signed in, you should see something similar to this.\n\n![New project](https://user-images.githubusercontent.com/37651620/159206701-23d739cc-37d7-47b6-bf39-63c4e04c3400.png)\n\nNow, select `New project` button. Fill in your project's required details and again click `Create Project` button and wait for the new database to load.\n\n![Create Project](https://user-images.githubusercontent.com/37651620/159207037-87f64dbe-baf6-45b3-81c5-c9c9d9dd65c3.png)\n\n![Creating project](https://user-images.githubusercontent.com/37651620/159207145-ef6283f0-96b2-4e24-b537-a449c8c2f8b5.png)\n\nAfter the supabase configured the project, your dashboard should look something similar to this.\n\n![SupabaseDashboard](https://user-images.githubusercontent.com/37651620/159207301-ba504656-531f-4782-a806-25b9d6169322.png)\n\n### Creating a connection URL\n\nFollow the steps outlined below to retrieve your database connection URL after your database has been successfully created. We'll need it to use Prisma in our Next.js app to query and create data.\n\n- **Step1** : Head over to the `Settings tab`(Located at the left side)\n\n![Setting Tab](https://user-images.githubusercontent.com/37651620/159232832-0ecb374c-1185-464d-b1ba-99b82c650119.png)\n\n- **Step2** : Click the `Database` tab in the sidebar (Located on the left side)\n\n![Database](https://user-images.githubusercontent.com/37651620/159232570-299da821-d7ec-4b35-bef8-7f6b55a86327.png)\n\n- **Step3** : Head over to the bottom of the page to find the `Connection string` section, then select `Nodejs` and copy the URL.\n\n![Connection string](https://user-images.githubusercontent.com/37651620/159337831-bed01290-cd64-4b48-8afa-a37fc35e2540.png)\n\n## Initializing Prisma\n\nPrisma is a next-generation ORM that can be used in Node.js and TypeScript applications to access a database. W eare going to use prisma fo our application because it includes all of the code we need to run our queries. It will save us a lot of time and keep us from having to write a bunch of boilerplate codes.\n\n### Installing prisma\n\n#### Prisma CLI installation\n\nThe Prisma command line interface (CLI) is the primary command-line interface for interacting with your Prisma project. It can create new project assets, generate Prisma Client, and analyze existing database structures via introspection to create your application models automatically.\n\n```\nnpm i prisma\n```\n\n![Prisma Installation](https://user-images.githubusercontent.com/37651620/159416387-5caea0cc-d44b-4cb4-ba9d-7cc2b02a086c.png)\n\n#### Initialize prisma\n\nOnce you've installed the Prisma CLI, run the following command to get `Prisma` started in your `Next.js` application. It will then create a `/prisma` directory and the `schema.prisma` file within it inside your particular project folder. so, inside it we will be adding all the configuration for our application.\n\n```\nnpx prisma init\n```\n\n![prismaSchema](https://user-images.githubusercontent.com/37651620/159417419-f3dbe24b-0041-4306-be72-9090142d5bc3.png)\n\n```js\n// prisma.schema\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider = \"postgresql\"\n  url      = env(\"DATABASE_URL\")\n}\n```\n\n\u003e Note: schema.prisma uses Prisma Schema Language(PSL)\n\n`Prisma-client-js`, the Prisma JavaScript client, is the configured client represented by the `generator` block.\n\n```js\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n```\n\nNext one is the the provider property of this block represents the type of database we want to use, and the connection url represents how Prisma connects to it.\n\n```js\ndatasource db {\n  provider = \"postgresql\"\n  url      = env(\"DATABASE_URL\")\n}\n```\n\n### Environment variable\n\nUsing environment variables in the schema allows you to keep secrets out of the schema file which in turn improves the portability of the schema by allowing you to use it in different environments. Environment variables is created automatically after we fire the `npx prisma init` command.\n\n\u003e Note: Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB (Preview) and CockroachDB (Preview).\n\n```\nDATABASE_URL=\"postgresql://test:test@localhost:5432/test?schema=foo\"\n```\n\nAs you can see, there is an `DATABASE_URL` variable with a dummy connection URL in this environment variable `.env`. So, replace this value with the connection string you obtained from Supabase.\n\n```\nDATABASE_URL=\"postgresql://postgres:[YOUR-PASSWORD]@db.bboujxbwamqvgypibdkh.supabase.co:5432/postgres\"\n```\n\n### Prisma schemas and models\n\nWe can begin working on our application's data models now that database is finally connected to your `Next.js`. In Prisma, our application models should be defined within the Prisma schema using the Prisma models. These models represent the entities of our application and are defined by the model blocks in the `schema.prisma` file. Each block contains several fields that represent the data for each entity. So, let's begin by creating the `Product` model, which will define the data schema for our products properties.\n\n#### Defining models\n\nModels represent the entities of your application domain. Models are represented by model blocks and define a number of fields. In this data model, `Product` is the model.\n\n```js\n// prisma.schema\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider = \"postgresql\"\n  url      = env(\"DATABASE_URL\")\n}\n\nmodel Product {\n  id          String   @id @default(cuid())\n  image       String?\n  title       String\n  description String\n  status      String?\n  price       Float\n  authenticity        Int?\n  returnPolicy        Int?\n  warranty       Int?\n  createdAt   DateTime @default(now())\n  updatedAt   DateTime @updatedAt\n}\n```\n\nEach field, as shown in our Product model, has at least a name and its type. To learn more about the Scalar types and Prisma schema refrences visit the following links .\n\n- [Data model](https://www.prisma.io/docs/concepts/components/prisma-schema/data-model#scalar-fields)\n- [Prisma schema](https://www.prisma.io/docs/concepts/components/prisma-schema)\n- [Prisma schema reference](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#model-fields)\n\n#### Generate Prisma Client\n\nAfter designning Prisma model, we can begin generating our Prisma Client. We'll need to use Prisma's JavaScript library later in the tutorial to interact with our data from within our `Next.js` app without having to write all of the SQL queries ourselves. But there's more to it. Prisma Client is, in fact, an auto-generated type-safe API designed specifically for our application which will gives us the JavaScript code we need to run queries on our data.\n\n- **Step 1**: Installing prisma client\n\n  ```\n  npm install @prisma/client\n  ```\n\n  ![PrismaClient](https://user-images.githubusercontent.com/37651620/159432404-3e880b0a-4058-44f9-b922-652e7e389d63.png)\n\n- **step2**: Generating Prisma client\n\n  ```\n  npx prisma generate\n  ```\n\n  ![Prisma Generate](https://user-images.githubusercontent.com/37651620/159432604-f6586811-262f-4d5f-b79b-5be9c4f1d5ff.png)\n\n#### The @prisma/client npm package\n\nThe @prisma/client npm package consists of two key parts:\n\n- The `@prisma/client` module itself, which only changes when you re-install the package\n- The `.prisma/client` folder, which is the default location for the unique Prisma Client generated from your schema\n\n`@prisma/client/index.d.ts` exports `.prisma/client`\n\nFinally, after you have done that inside your `./node_modules` folder, you should now find the generated Prisma Client code.\n\n![PrismaGenerate](https://user-images.githubusercontent.com/37651620/159435415-e4765d0e-b5c5-4fc5-9ad2-1ddf381539a4.png)\n\n\u003e Note: You need to re-run the prisma generate command after every change that's made to your Prisma schema to update the generated Prisma Client code.\n\nHere is a graphical illustration of the typical workflow for the Prisma Client generation:\n\n![WorkFlow](https://user-images.githubusercontent.com/37651620/159502666-d8c85d78-02a3-4af0-8f4e-6d29e9193247.png)\n\n\u003e Note also that prisma generate is automatically invoked when you're installing the `@prisma/client` npm package.\n\nThe Prisma Client is generated from the Prisma schema and is unique to your project. Each time you change the schema and run prisma generate, the client code changes itself.\n\n![PrismaClient](https://user-images.githubusercontent.com/37651620/159506561-c361008c-6516-44fe-8d98-07ec4688cc38.png)\n\nPruning in `Node.js` package managers has no effect on the `.prisma` folder.\n\n## Creating a table in `Supabase`\n\nIf you look at your database in Supabase, you'll notice there is no table inside it. It's because we haven't yet created the `Product` table.\n\n![DashBoardScrrenshot](https://user-images.githubusercontent.com/37651620/159444118-7a373b58-b972-4bb9-9c8b-ddfd274526bd.png)\n\nThe Prisma model we defined in our `schema.prisma` file has not yet been reflected in our database. As a result, we must manually push changes to our data model to our database.\n\n### Pushing the data model\n\nPrisma makes it really very easy to synchonize the schema with our database.So to do that follow the command listed below.\n\n```\nnpx prisma db push\n```\n\n![PrismaDB Push](https://user-images.githubusercontent.com/37651620/159443639-7b95dd91-7a02-4bd1-8603-749ccfa5a0ab.png)\n\n![PushDatabase](https://user-images.githubusercontent.com/37651620/159445495-f28b10b6-7bb1-49d3-a6ab-e044950b51c6.png)\n\nThis command is only good for prototyping on the schemas locally.\n\nOR,\n\n```\nnpx prisma migrate dev\n```\n\nThis method (`npx prisma migrate dev`) will be used in this tutorial because it is very useful in that it allows us to directly sync our Prisma schema with our database while also allowing us to easily track the changes that we make.\n\nSo, to begin using Prisma Migrate, enter the following command into the command prompt and after that enter a name for this first migration when prompted.\n\n![PrismaMigrate](https://user-images.githubusercontent.com/37651620/159447379-c54179e1-e1d1-4f6b-a8fc-d73059b360c4.png)\n\nAfter you have completed this process successfully, prisma will automatically generate SQL database migration files, and you should be able to see the SQL which should look something like this if you look inside the `prisma` folder.\n\n![FolderStructure](https://user-images.githubusercontent.com/37651620/159448032-a0259fb8-ed70-4275-9676-41f8b6fbc57f.png)\n\n```sql\n-- CreateTable\nCREATE TABLE \"Product\" (\n    \"id\" TEXT NOT NULL,\n    \"image\" TEXT,\n    \"title\" TEXT NOT NULL,\n    \"description\" TEXT NOT NULL,\n    \"status\" TEXT NOT NULL,\n    \"price\" DOUBLE PRECISION NOT NULL,\n    \"authenticity\" INTEGER,\n    \"returnPolicy\" INTEGER,\n    \"warranty\" INTEGER,\n    \"createdAt\" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    \"updatedAt\" TIMESTAMP(3) NOT NULL,\n\n    CONSTRAINT \"Product_pkey\" PRIMARY KEY (\"id\")\n);\n---\n```\n\nFinally, check the Supabase dashboard to see if everything has been successfully synced.\n\n![Supabase](https://user-images.githubusercontent.com/37651620/159447800-82feed93-398d-4a8a-89c1-fbf10a4adf74.png)\n\n## Prisma Studio\n\nPrisma Studio is a visual interface to the data residing inside your database where you can use to quickly visualize and manipulate the data. The cool thing about it is that it runs in entirely on your browser and you don't need to set up any connections because it's already comes with the prisma package. Not only that, from the studio, you can quickly open all of your application's models and interact with them directly via. studio itself.\n\n\u003e Note: There is also desktop application available to download\n\n### Launching Prisma Studio\n\nLaunching the prisma studio is really very easy. Literally all you have to do is run the following command from a Prisma project.\n\n```\nnpx prisma studio\n```\n\n![PrismaStudio](https://user-images.githubusercontent.com/37651620/159463409-b9614ad2-5c63-4519-98f2-d2ab791cc604.png)\n\nNow, open your browser and head over to `http://localhost:5555/`. You should be able to see the single table that we've created previously if you've followed all of the steps correctly.\n\n![PrismaStudio](https://user-images.githubusercontent.com/37651620/159463579-2019f826-22c6-484c-a877-319ec6abe170.png)\n\n![PrismaStudio](https://user-images.githubusercontent.com/37651620/159463739-764e709b-9ce9-469b-ada2-dce371a8efad.png)\n\n### Manually adding the records\n\nLets manually add some records and save the changes that we made.\n\n![PrismaStudio](https://user-images.githubusercontent.com/37651620/159464112-a0575030-4b00-455a-8698-48983f534d39.png)\n\n![DataFilled](https://user-images.githubusercontent.com/37651620/159473084-776a272e-0186-42eb-8905-27e6de0ce1c4.png)\n\nFinally, lets create a functionality to access that data from within our Next.js app, where we can create new records, update existing ones, and delete old ones.\n\n## Interacting with data using Next.js\n\nYou should see some demo datas if you look at the `Product` page of your application.\n\n![LandingShopButton](https://user-images.githubusercontent.com/37651620/159481862-f5f5cb2f-52f4-4b52-80a3-2cb63f41a43d.png)\n\n![Product Page](https://user-images.githubusercontent.com/37651620/159481958-aeaab5e3-a495-47f8-acce-980701262a5f.png)\n\nNow, open the file `pages/products.js`, file which represents our app's product page.\n\n```js\n// pages/products.js\nimport Layout from \"@/components/Layout\";\nimport Grid from \"@/components/Grid\";\n\nimport products from \"products.json\";\n\nexport default function Products() {\n  return (\n    \u003cLayout\u003e\n      \u003cdiv className=\"mt-8 p-5\"\u003e\n        \u003cGrid products={products} /\u003e\n      \u003c/div\u003e\n    \u003c/Layout\u003e\n  );\n}\n```\n\nAs you can see, products data is comming from `products.json` file.\n\n```json\n// products.json\n[\n  {\n    \"id\": \"001\",\n    \"image\": \"/products/ballpen_300.png\",\n    \"title\": \"Ball Pen\",\n    \"description\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\",\n    \"authenticity\": 100,\n    \"returnPolicy\": 1,\n    \"status\": \"new\",\n    \"warranty\": 1,\n    \"price\": 50\n  },\n  {\n    \"id\": \"002\",\n    \"image\": \"/products/actioncamera_300.png\",\n    \"title\": \"Go-pro cam\",\n    \"description\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\",\n    \"authenticity\": 100,\n    \"returnPolicy\": 1,\n    \"status\": \"new\",\n    \"warranty\": 1,\n    \"price\": 30\n  },\n  {\n    \"id\": \"003\",\n    \"image\": \"/products/alarmclock_300.png\",\n    \"title\": \"Alarm Clock\",\n    \"description\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\",\n    \"authenticity\": 100,\n    \"returnPolicy\": 1,\n    \"status\": \"new\",\n    \"warranty\": 1,\n    \"price\": 20\n  },\n  {\n    \"id\": \"004\",\n    \"image\": \"/products/bangle_600.png\",\n    \"title\": \"Bangle\",\n    \"description\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\",\n    \"authenticity\": 100,\n    \"returnPolicy\": 1,\n    \"status\": \"new\",\n    \"warranty\": 2,\n    \"price\": 200\n  },\n  {\n    \"id\": \"005\",\n    \"image\": \"/products/bed_600.png\",\n    \"title\": \"Large Bed\",\n    \"description\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\",\n    \"authenticity\": 100,\n    \"returnPolicy\": 1,\n    \"status\": \"out of stock!\",\n    \"warranty\": 1,\n    \"price\": 105\n  },\n  {\n    \"id\": \"006\",\n    \"image\": \"/products/binderclip_600.png\",\n    \"title\": \"Binder clip\",\n    \"description\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\",\n    \"authenticity\": 100,\n    \"returnPolicy\": 2,\n    \"status\": \"new\",\n    \"warranty\": 1,\n    \"price\": 2\n  },\n  {\n    \"id\": \"007\",\n    \"image\": \"/products/beyblade_600.png\",\n    \"title\": \"BeyBlade Burst\",\n    \"description\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\",\n    \"authenticity\": 100,\n    \"returnPolicy\": 1,\n    \"status\": \"out of stock!\",\n    \"warranty\": 1,\n    \"price\": 15\n  },\n  {\n    \"id\": \"008\",\n    \"image\": \"/products/boxinggloves_600.png\",\n    \"title\": \"Boxing gloves\",\n    \"description\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\",\n    \"authenticity\": 100,\n    \"returnPolicy\": 2,\n    \"status\": \"new\",\n    \"warranty\": 1,\n    \"price\": 45\n  }\n]\n```\n\nThis data \u0026 information is then passed as a prop from the `Product` component to the `Grid` component. The `Grid` component is then in charge of rendering those data as a grid of Card on the screen.\n\n```js\n// Products.js\nimport PropTypes from \"prop-types\";\nimport Card from \"@/components/Card\";\nimport { ExclamationIcon } from \"@heroicons/react/outline\";\n\nconst Grid = ({ products = [] }) =\u003e {\n  const isEmpty = products.length === 0;\n\n  return isEmpty ? (\n    \u003cp className=\"text-purple-700 bg-amber-100 px-4 rounded-md py-2 max-w-max inline-flex items-center space-x-1\"\u003e\n      \u003cExclamationIcon className=\"shrink-0 w-5 h-5 mt-px\" /\u003e\n      \u003cspan\u003eNo data to be displayed.\u003c/span\u003e\n    \u003c/p\u003e\n  ) : (\n    \u003cdiv className=\"grid md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6\"\u003e\n      {products.map((product) =\u003e (\n        \u003cCard key={product.id} {...product} onClickFavorite={toggleFavorite} /\u003e\n      ))}\n    \u003c/div\u003e\n  );\n};\n\nGrid.propTypes = {\n  products: PropTypes.array,\n};\n\nexport default Grid;\n```\n\nNow we want to retrieve data from our database, and we'll do so using Server-Side Rendering (SSR). The ability of an application to convert HTML files on the server into a fully rendered HTML page for the client is known as server-side rendering (SSR). The web browser sends a request for information to the server, which responds immediately by sending the client a fully rendered page.\n\n![SSR](https://user-images.githubusercontent.com/37651620/159494240-00db1ac9-7f1d-4dbe-88ef-0bdd3c196ac5.png)\n\nSo, in order to use Server Side Rendering(SSR) with `Next.js`, we must export an asynchronous function `getServerSideProps` from within the file, which exports the page where we want to render out our data. The data returned by the `getServerSideProps` function will then be used by `Next.js` to pre-render our page on each individual request. Let's get started and export this function from our applicartion's `Prodcuts` page.\n\n```js\n// pages/products.js\nimport Layout from \"@/components/Layout\";\nimport Grid from \"@/components/Grid\";\n\nimport products from \"products.json\";\n\nexport async function getServerSideProps() {\n  return {\n    props: {\n      // props for the Home component\n    },\n  };\n}\n\nexport default function Products() {\n  return (\n    \u003cLayout\u003e\n      \u003cdiv className=\"mt-8 p-5\"\u003e\n        \u003cGrid products={products} /\u003e\n      \u003c/div\u003e\n    \u003c/Layout\u003e\n  );\n}\n```\n\nTo get the data from supabase, import and instantiate the `generated Prisma client`.\n\n```jsx\n// pages/products.js\nimport Layout from \"@/components/Layout\";\nimport Grid from \"@/components/Grid\";\nimport { PrismaClient } from \"@prisma/client\";\n\nimport products from \"products.json\";\n\nconst prisma = new PrismaClient();\n\nexport async function getServerSideProps() {\n  return {\n    props: {\n      // props for the Home component\n    },\n  };\n}\n\nexport default function Products() {\n  return (\n    \u003cLayout\u003e\n      \u003cdiv className=\"mt-8 p-5\"\u003e\n        \u003cGrid products={products} /\u003e\n      \u003c/div\u003e\n    \u003c/Layout\u003e\n  );\n}\n```\n\nNow, Using the `findMany` query, we can get all of the records in our Product table:\n\n```jsx\n// pages/products.js\nimport Layout from \"@/components/Layout\";\nimport Grid from \"@/components/Grid\";\nimport { PrismaClient } from \"@prisma/client\";\n\nconst prisma = new PrismaClient();\n\nexport async function getServerSideProps() {\n  const products = await prisma.product.findMany();\n  return {\n    props: {\n      products: JSON.parse(JSON.stringify(products)),\n    },\n  };\n}\n\nexport default function Products({ products = [] }) {\n  return (\n    \u003cLayout\u003e\n      \u003cdiv className=\"mt-8 p-5\"\u003e\n        \u003cGrid products={products} /\u003e\n      \u003c/div\u003e\n    \u003c/Layout\u003e\n  );\n}\n```\n\nSimply re-run the application, but if you get an error that looks like the one below, you'll need to regenerate the prisma and then re-run the server.\n\n![Error](https://user-images.githubusercontent.com/37651620/159522918-6f09bba1-2577-4118-9ed2-b597bcb90794.png)\n\nAs you can see, its fixed now\n\n![Fixed error](https://user-images.githubusercontent.com/37651620/159523214-7b6ff048-327d-4e71-b9f3-f5360102b6f9.png)\n\nFinally, your application should resemble something like this:\n\n![Application Final Demno](https://user-images.githubusercontent.com/37651620/159528273-977d0514-9618-441a-9f7e-3ad461159fa9.png)\n\nLets give users the functionality to actually create records from the application itself. So, first step is to actually create.\n\n## Create a new records\n\nHead over to the `pages/` folder and make a new file called `addProduct.js`.\n\n![Folder structure](https://user-images.githubusercontent.com/37651620/159563363-3253d40c-e2e0-4ed7-99dd-3f90e642791e.png)\n\n```js\n// addProducts.js\nimport Layout from \"@/components/Layout\";\nimport ProductList from \"@/components/ProductList\";\n\nconst addProducts = () =\u003e {\n  const createProduct = () =\u003e null;\n\n  return (\n    \u003cLayout\u003e\n      \u003cdiv className=\"max-w-screen-xl mx-auto flex-col\"\u003e\n        \u003ch1 className=\"text-3xl font-medium text-gray-200 justify-center\"\u003e\n          Add your Products\n        \u003c/h1\u003e\n        \u003cdiv className=\"mt-8\"\u003e\n          \u003cProductList\n            buttonText=\"Add Product\"\n            redirectPath=\"/products\"\n            onSubmit={createProduct}\n          /\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/Layout\u003e\n  );\n};\n\nexport default addProducts;\n```\n\nAfter that head over to the `ProductList` component and make the following changes to that component.\n\n```js\n//components/ProductList.js\nimport { useState } from \"react\";\nimport { useRouter } from \"next/router\";\nimport PropTypes from \"prop-types\";\nimport * as Yup from \"yup\";\nimport { toast } from \"react-hot-toast\";\nimport { Formik, Form } from \"formik\";\nimport Input from \"@/components/Input\";\nimport AddProductImage from \"@/components/AddProductImage\";\n\nconst ProductSchema = Yup.object().shape({\n  title: Yup.string().trim().required(),\n  description: Yup.string().trim().required(),\n  status: Yup.string().trim().required(),\n  price: Yup.number().positive().integer().min(1).required(),\n  authenticity: Yup.number().positive().integer().min(1).required(),\n  returnPolicy: Yup.number().positive().integer().min(1).required(),\n  warranty: Yup.number().positive().integer().min(1).required(),\n});\n\nconst ProductList = ({\n  initialValues = null,\n  redirectPath = \"\",\n  buttonText = \"Submit\",\n  onSubmit = () =\u003e null,\n}) =\u003e {\n  const router = useRouter();\n\n  const [disabled, setDisabled] = useState(false);\n  const [imageUrl, setImageUrl] = useState(initialValues?.image ?? \"\");\n\n  const upload = async (image) =\u003e {\n    // TODO: Upload image to remote storage\n  };\n\n  const handleOnSubmit = async (values = null) =\u003e {\n    let toastId;\n    try {\n      setDisabled(true);\n      toastId = toast.loading(\"Submitting...\");\n      // Submit data\n      if (typeof onSubmit === \"function\") {\n        await onSubmit({ ...values, image: imageUrl });\n      }\n      toast.success(\"Successfully submitted\", { id: toastId });\n      // Redirect user\n      if (redirectPath) {\n        router.push(redirectPath);\n      }\n    } catch (e) {\n      toast.error(\"Unable to submit\", { id: toastId });\n      setDisabled(false);\n    }\n  };\n\n  const { image, ...initialFormValues } = initialValues ?? {\n    image: \"\",\n    title: \"\",\n    description: \"\",\n    status: \"\",\n    price: 0,\n    authenticity: 1,\n    returnPolicy: 1,\n    warranty: 1,\n  };\n\n  return (\n    \u003cdiv\u003e\n      \u003cFormik\n        initialValues={initialFormValues}\n        validationSchema={ProductSchema}\n        validateOnBlur={false}\n        onSubmit={handleOnSubmit}\n      \u003e\n        {({ isSubmitting, isValid }) =\u003e (\n          \u003cForm className=\"space-y-6\"\u003e\n            \u003cdiv className=\"space-y-6\"\u003e\n              \u003cInput\n                name=\"title\"\n                type=\"text\"\n                label=\"Title\"\n                placeholder=\"Entire your product name...\"\n                disabled={disabled}\n              /\u003e\n\n              \u003cInput\n                name=\"description\"\n                type=\"textarea\"\n                label=\"Description\"\n                placeholder=\"Enter your product description....\"\n                disabled={disabled}\n                rows={3}\n              /\u003e\n\n              \u003cInput\n                name=\"status\"\n                type=\"text\"\n                label=\"Status(new/out-of-stock/used)\"\n                placeholder=\"Enter your product status....\"\n                disabled={disabled}\n              /\u003e\n\n              \u003cInput\n                name=\"price\"\n                type=\"number\"\n                min=\"0\"\n                label=\"Price of the product...\"\n                placeholder=\"100\"\n                disabled={disabled}\n              /\u003e\n\n              \u003cdiv className=\"justify-center\"\u003e\n                \u003cInput\n                  name=\"authenticity\"\n                  type=\"number\"\n                  min=\"0\"\n                  label=\"authenticity(%)\"\n                  placeholder=\"2\"\n                  disabled={disabled}\n                /\u003e\n                \u003cInput\n                  name=\"returnPolicy\"\n                  type=\"number\"\n                  min=\"0\"\n                  label=\"returnPolicy(? years)\"\n                  placeholder=\"1\"\n                  disabled={disabled}\n                /\u003e\n                \u003cInput\n                  name=\"warranty\"\n                  type=\"number\"\n                  min=\"0\"\n                  label=\"warranty(? years)\"\n                  placeholder=\"1\"\n                  disabled={disabled}\n                /\u003e\n              \u003c/div\u003e\n            \u003c/div\u003e\n\n            \u003cdiv className=\"flex justify-center\"\u003e\n              \u003cbutton\n                type=\"submit\"\n                disabled={disabled || !isValid}\n                className=\"bg-success text-white py-2 px-6 rounded-md focus:outline-none focus:ring-4 focus:ring-teal-600 focus:ring-opacity-50 hover:bg-teal-500 transition disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-teal-600\"\n              \u003e\n                {isSubmitting ? \"Submitting...\" : buttonText}\n              \u003c/button\u003e\n            \u003c/div\u003e\n          \u003c/Form\u003e\n        )}\n      \u003c/Formik\u003e\n      \u003cdiv className=\"mb-6 max-w-full\"\u003e\n        \u003cAddProductImage\n          initialImage={{ src: image, alt: initialFormValues.title }}\n          onChangePicture={upload}\n        /\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n\nProductList.propTypes = {\n  initialValues: PropTypes.shape({\n    image: PropTypes.string,\n    title: PropTypes.string,\n    description: PropTypes.string,\n    status: PropTypes.string,\n    price: PropTypes.number,\n    authenticity: PropTypes.number,\n    returnPolicy: PropTypes.number,\n    warranty: PropTypes.number,\n  }),\n  redirectPath: PropTypes.string,\n  buttonText: PropTypes.string,\n  onSubmit: PropTypes.func,\n};\n\nexport default ProductList;\n```\n\nAfter that, go to the `AddProductImage` file inside the component folder and copy the following code.\n\n```js\n// AddProductImage.js\nimport { useState, useRef } from \"react\";\nimport PropTypes from \"prop-types\";\nimport Image from \"next/image\";\nimport toast from \"react-hot-toast\";\nimport classNames from \"classnames\";\nimport { CloudUploadIcon } from \"@heroicons/react/outline\";\n\nconst AddProductImage = ({\n  label = \"Image\",\n  initialImage = null,\n  objectFit = \"cover\",\n  accept = \".png, .jpg, .jpeg, .gif .jiff\",\n  sizeLimit = 10 * 1024 * 1024,\n  onChangePicture = () =\u003e null,\n}) =\u003e {\n  const pictureRef = useRef();\n  const [image, setImage] = useState(initialImage ?? null);\n  const [updatingPicture, setUpdatingPicture] = useState(false);\n  const [pictureError, setPictureError] = useState(null);\n\n  const handleOnChangePicture = (e) =\u003e {\n    const file = e.target.files[0];\n    const reader = new FileReader();\n    const fileName = file?.name?.split(\".\")?.[0] ?? \"New file\";\n\n    reader.addEventListener(\n      \"load\",\n      async function () {\n        try {\n          setImage({ src: reader.result, alt: fileName });\n          if (typeof onChangePicture === \"function\") {\n            await onChangePicture(reader.result);\n          }\n        } catch (err) {\n          toast.error(\"Unable to update image\");\n        } finally {\n          setUpdatingPicture(false);\n        }\n      },\n      false\n    );\n\n    if (file) {\n      if (file.size \u003c= sizeLimit) {\n        setUpdatingPicture(true);\n        setPictureError(\"\");\n        reader.readAsDataURL(file);\n      } else {\n        setPictureError(\"File size is exceeding 10MB.\");\n      }\n    }\n  };\n\n  const handleOnClickPicture = () =\u003e {\n    if (pictureRef.current) {\n      pictureRef.current.click();\n    }\n  };\n\n  return (\n    \u003cdiv className=\"flex flex-col space-y-2\"\u003e\n      \u003clabel className=\"text-gray-200 \"\u003e{label}\u003c/label\u003e\n\n      \u003cbutton\n        disabled={updatingPicture}\n        onClick={handleOnClickPicture}\n        className={classNames(\n          \"relative aspect-video overflow-hidden rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition group focus:outline-none\",\n          image?.src\n            ? \"hover:opacity-50 disabled:hover:opacity-100\"\n            : \"border-2 border-dotted hover:border-gray-400 focus:border-gray-400 disabled:hover:border-gray-200\"\n        )}\n      \u003e\n        {image?.src ? (\n          \u003cImage\n            src={image.src}\n            alt={image?.alt ?? \"\"}\n            layout=\"fill\"\n            objectFit={objectFit}\n          /\u003e\n        ) : null}\n\n        \u003cdiv className=\"flex items-center justify-center\"\u003e\n          {!image?.src ? (\n            \u003cdiv className=\"flex flex-col items-center space-y-2\"\u003e\n              \u003cdiv className=\"shrink-0 rounded-full p-2 bg-gray-200 group-hover:scale-110 group-focus:scale-110 transition\"\u003e\n                \u003cCloudUploadIcon className=\"w-4 h-4 text-gray-500 transition\" /\u003e\n              \u003c/div\u003e\n              \u003cspan className=\"text-xs font-semibold text-gray-500 transition\"\u003e\n                {updatingPicture\n                  ? \"Image Uploading...\"\n                  : \"Upload product Image\"}\n              \u003c/span\u003e\n            \u003c/div\u003e\n          ) : null}\n          \u003cinput\n            ref={pictureRef}\n            type=\"file\"\n            accept={accept}\n            onChange={handleOnChangePicture}\n            className=\"hidden\"\n          /\u003e\n        \u003c/div\u003e\n      \u003c/button\u003e\n\n      {pictureError ? (\n        \u003cspan className=\"text-red-600 text-sm\"\u003e{pictureError}\u003c/span\u003e\n      ) : null}\n    \u003c/div\u003e\n  );\n};\n\nAddProductImage.propTypes = {\n  label: PropTypes.string,\n  initialImage: PropTypes.shape({\n    src: PropTypes.string,\n    alt: PropTypes.string,\n  }),\n  objectFit: PropTypes.string,\n  accept: PropTypes.string,\n  sizeLimit: PropTypes.number,\n  onChangePicture: PropTypes.func,\n};\n\nexport default AddProductImage;\n```\n\nThis `addProduct` component renders the entire page's layout, which consist of a form from where you can add the product details and informations.\n\n![Demo](https://user-images.githubusercontent.com/37651620/159575908-1488990b-6982-446d-901a-27d72aeff82e.png)\n\n### API endpoint\n\nLet's actually create a API endpoint that will actually create a new record on our database via `addProduct` function.\n\n```js\nconst createProduct = () =\u003e null;\n```\n\nBut first, within our `Next.js` application project, let's create an `API` endpoint to handle our `POST` request for creating new records. `Next.js` provides a file based API routing so any file in the `pages/api` folder is mapped to `/api/*` and treated as an API endpoint rather than a page. They're only `server-side` bundles, so they won't add to the size of your `client-side` bundle. So, create a file name called `products.js` inside the `pages/api` folder and inside it create a request handler fucntion like shown below.\n\n```js\nexport default async function handler(req, res) {}\n```\n\n#### Handling `POST` request for `products`\n\nBefore we go any further, use `req.method` to check the `HTTP` method of the request inside that `request handler` function. After that, return a 405 status code to the client becasue we are not handlling any kind of HTTP method.\n\n```js\n// pages/api/products.js\nexport default async function handler(req, res) {\n  if (req.method === \"POST\") {\n    // TODO\n  } else {\n    res.setHeader(\"Allow\", [\"POST\"]);\n    res\n      .status(405)\n      .json({ message: `HTTP method ${req.method} is not supported.` });\n  }\n}\n```\n\n#### Adding new records with Prisma Client\n\nNow, lets use Prisma Client to create a new `Product` record in the database using the data from the current HTTP request.\n\n```js\n// pages/api/products.js\nexport default async function handler(req, res) {\n  if (req.method === \"POST\") {\n    const {\n      image,\n      title,\n      description,\n      status,\n      price,\n      authenticity,\n      returnPolicy,\n      warranty,\n    } = req.body;\n  } else {\n    res.setHeader(\"Allow\", [\"POST\"]);\n    res\n      .status(405)\n      .json({ message: `HTTP method ${req.method} is not supported.` });\n  }\n}\n```\n\nAfter that, lets actually initialize `Prisma` and call the `create` function that prisma provides.\n\n```js\nimport { PrismaClient } from \"@prisma/client\";\n\nconst prisma = new PrismaClient();\n\nexport default async function handler(req, res) {\n  if (req.method === \"POST\") {\n    const {\n      image,\n      title,\n      description,\n      status,\n      price,\n      authenticity,\n      returnPolicy,\n      warranty,\n    } = req.body;\n\n    const home = await prisma.product.create({\n      data: {\n        image,\n        title,\n        description,\n        status,\n        price,\n        authenticity,\n        returnPolicy,\n        warranty,\n      },\n    });\n  } else {\n    res.setHeader(\"Allow\", [\"POST\"]);\n    res\n      .status(405)\n      .json({ message: `HTTP method ${req.method} is not supported.` });\n  }\n}\n```\n\nFinally lets add some try catch block to Handle the error.\n\n```js\n// pages/api/products.js\nimport { PrismaClient } from \"@prisma/client\";\nconst prisma = new PrismaClient();\n\nexport default async function handler(req, res) {\n  if (req.method === \"POST\") {\n    try {\n      const {\n        image,\n        title,\n        description,\n        status,\n        price,\n        authenticity,\n        returnPolicy,\n        warranty,\n      } = req.body;\n\n      const product = await prisma.product.create({\n        data: {\n          image,\n          title,\n          description,\n          status,\n          price,\n          authenticity,\n          returnPolicy,\n          warranty,\n        },\n      });\n      res.status(200).json(product);\n    } catch (e) {\n      res.status(500).json({ message: \"Something went wrong\" });\n    }\n  } else {\n    res.setHeader(\"Allow\", [\"POST\"]);\n    res\n      .status(405)\n      .json({ message: `HTTP method ${req.method} is not supported.` });\n  }\n}\n```\n\nNow that we've created our `API`, let's call the API endpoint. To do so, open the `addProduct.js` file in the `pages` folder and make the following changes to the code, but first, we'll need to install the `axios` package, so do that first.\n\n```bash\nnpm i axios\n```\n\nOR\n\n```bash\nyarn add axios\n```\n\n![Axios](https://user-images.githubusercontent.com/37651620/159638586-5cf82100-c3dc-4dbc-a4a8-7188408aba4c.png)\n\n```js\n//pages/addProducts.js\nimport Layout from \"@/components/Layout\";\nimport ProductList from \"@/components/ProductList\";\n\nconst addProducts = () =\u003e {\n  const createProduct = () =\u003e (data) =\u003e axios.post(\"/api/products\", data);\n\n  return (\n    \u003cLayout\u003e\n      \u003cdiv className=\"max-w-screen-xl mx-auto flex-col\"\u003e\n        \u003ch1 className=\"text-3xl font-medium text-gray-200 justify-center\"\u003e\n          Add your Products\n        \u003c/h1\u003e\n        \u003cdiv className=\"mt-8\"\u003e\n          \u003cProductList\n            buttonText=\"Add Product\"\n            redirectPath=\"/products\"\n            onSubmit={createProduct}\n          /\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/Layout\u003e\n  );\n};\n\nexport default addProducts;\n```\n\nNow lets re-run the server again.\n\n![image](https://user-images.githubusercontent.com/37651620/159643456-282dfb5e-2963-44f7-920e-1db0cc4cfa4e.png)\n\nAfter that head over to your browser and go to the `http://localhost:3000/addProducts` route and fill out all the product information and `Submit` it.\n\n![Demo](https://user-images.githubusercontent.com/37651620/159903073-4fd2e7ed-7317-4011-9700-9305fd8e875e.png)\n![Demo](https://user-images.githubusercontent.com/37651620/159903115-4347de95-d5e1-4747-8f45-acae6b99d969.png)\n![Demo](https://user-images.githubusercontent.com/37651620/159903149-710afad5-015b-4dc3-b3e6-8281a300820f.png)\n\nIt will automatically redirect you to the `/products` page and you should be able to see the product that you just added.\n\n![Demo](https://user-images.githubusercontent.com/37651620/159903167-ae9c4c76-aab3-4fff-880b-fb1a1e6a1d70.png)\n\n## Pre-rendering the pages\n\nWe've used the `getServerSideProps` function to pre-render the `product` of our app using `Server-Side Rendering(SSR)`. Next.js, on the other hand, comes with a `built-in` pre-rendering method called `Static Generation (SSG)`.\n\nWhen a page uses Static Generation, the HTML for that page is generated during the build process. That means that when you run next build in production, the page HTML is generated. Each request will then be served with the same HTML. A `CDN` can cache it. You can statically generate pages with or without data using `Next.js`.\n\n![SSG](https://user-images.githubusercontent.com/37651620/159921224-aadedf3e-aaf1-4457-a5f9-486289bd28c6.png)\n\nWe can use different `pre-rendering` techniques on our applications when we use a framework like `Next.js`. For something more simple and non-dynamic, we can use `static site generation(SSG)`. For dynamic content and more complex pages, we can use `server-side rendering(SSR)` .\n\n### Dynamic Routing with SSG\n\nWe can still statically generate pages with SSG after fetching some external data during the build process, even if SSG generates HTML at build time. [learn more about static generation and dynamic routing](https://nextjs.org/docs/basic-features/pages).\n\nLet's get data at build time by exporting an `async` function called `getStaticProps` from the pages we want to statically generate.\n\n### For Example,\n\n```js\n// posts will be populated at build time by getStaticProps()\nfunction Blog({ posts }) {\n  return (\n    \u003cul\u003e\n      {posts.map((post) =\u003e (\n        \u003cli\u003e{post.title}\u003c/li\u003e\n      ))}\n    \u003c/ul\u003e\n  );\n}\n// This function gets called at build time on server-side.\n// It won't be called on client-side, so you can even do\n// direct database queries.\nexport async function getStaticProps() {\n  // Call an external API endpoint to get posts.\n  // You can use any data fetching library\n  const res = await fetch(\"https://.../posts\");\n  const posts = await res.json();\n  // By returning { props: { posts } }, the Blog component\n  // will receive `posts` as a prop at build time\n  return {\n    props: {\n      posts,\n    },\n  };\n}\nexport default Blog;\n```\n\nLet's put Static Generation(SSG) to work in our application. The pages that render each individual `Product` listing are the ones that we'll statically generate at the build time. However, because `product` listings are generated through the users, we could end up with massive amount of pages. As a result, we won't be able to define those routes using predefined paths. Otherwise, we'll end up with a slew of useless files cluttering up our project.\n\nWe can easily create dynamic routes in `Next.js`. We just need to add brackets to a page's filename, `[id].js`, to create a dynamic route. However, in our project, we will place that in the `Products` folder. As a result, any route's `ids` will be matched with their specific id value, and the id value will be available inside the React component that renders the associated page.\n\nNow, go to the pages folder and make a new folder called `products`, then make a new file called `[id].js` inside it.\n\n![products](https://user-images.githubusercontent.com/37651620/159926080-e55b1760-02eb-41a6-a8ac-84a4c2c8b49b.png)\n\nAnd finally paste the following code inside that file.\n\n```jsx\n// pages/products/[id].jsx\nimport Image from \"next/image\";\nimport Layout from \"@/components/Layout\";\n\nconst ListedProducts = (product = null) =\u003e {\n  return (\n    \u003cLayout\u003e\n      \u003cdiv className=\"max-w-screen-lg mx-auto\"\u003e\n        \u003cdiv className=\"mt-6 relative aspect-video bg-gray-400 rounded-lg shadow-md overflow-hidden\"\u003e\n          {product?.image ? (\n            \u003cImage\n              src={product.image}\n              alt={product.title}\n              layout=\"fill\"\n              objectFit=\"cover\"\n            /\u003e\n          ) : null}\n        \u003c/div\u003e\n        \u003cdiv className=\"flex flex-col sm:flex-row sm:justify-between sm:space-x-4 space-y-4 pt-10\"\u003e\n          \u003cdiv\u003e\n            \u003ch1 className=\"text-2xl font-semibold truncate\"\u003e\n              {product?.title ?? \"\"}\n            \u003c/h1\u003e\n            \u003col className=\"inline-flex items-center space-x-1 text-info\"\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.status ?? 0} product\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.authenticity ?? 0}% Authentic\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.returnPolicy ?? 0} year return policy\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.warranty ?? 0} year warranty\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n              \u003c/li\u003e\n            \u003c/ol\u003e\n            \u003cp className=\"mt-8 text-lg\"\u003e{product?.description ?? \"\"}\u003c/p\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/Layout\u003e\n  );\n};\n\nexport default ListedProducts;\n```\n\nNow, let's actually provide the lists of paths of the pages that we want to statically generate, and let's actually fetch some data and match it with the numbers of paths. To do so, we must provide the paths to Next.js that we want to pre-render at build time.This function should return all the paths of the pages to pre-render at build time, along with the corresponding `id` value in the returned object's params property. So for that, we'll be using Prisma to retrieve the IDs for all of the `products` residing on our database.\n\n```js\n// pages/products/[id].jsx\nimport Image from \"next/image\";\nimport Layout from \"@/components/Layout\";\nimport { PrismaClient } from \"@prisma/client\";\n// Instantiate Prisma Client\nconst prisma = new PrismaClient();\n\nconst ListedProducts = (product = null) =\u003e {\n  return (\n    \u003cLayout\u003e\n      \u003cdiv className=\"max-w-screen-lg mx-auto\"\u003e\n        \u003cdiv className=\"mt-6 relative aspect-video bg-gray-400 rounded-lg shadow-md overflow-hidden\"\u003e\n          {product?.image ? (\n            \u003cImage\n              src={product.image}\n              alt={product.title}\n              layout=\"fill\"\n              objectFit=\"cover\"\n            /\u003e\n          ) : null}\n        \u003c/div\u003e\n        \u003cdiv className=\"flex flex-col sm:flex-row sm:justify-between sm:space-x-4 space-y-4 pt-10\"\u003e\n          \u003cdiv\u003e\n            \u003ch1 className=\"text-2xl font-semibold truncate\"\u003e\n              {product?.title ?? \"\"}\n            \u003c/h1\u003e\n            \u003col className=\"inline-flex items-center space-x-1 text-info\"\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.status ?? 0} product\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.authenticity ?? 0}% Authentic\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.returnPolicy ?? 0} year return policy\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.warranty ?? 0} year warranty\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n              \u003c/li\u003e\n            \u003c/ol\u003e\n            \u003cp className=\"mt-8 text-lg\"\u003e{product?.description ?? \"\"}\u003c/p\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/Layout\u003e\n  );\n};\n\nexport async function getStaticPaths() {\n  const products = await prisma.product.findMany({\n    select: { id: true },\n  });\n\n  return {\n    paths: products.map((product) =\u003e ({\n      params: { id: product.id },\n    })),\n    fallback: false,\n  };\n}\n\nexport default ListedProducts;\n```\n\nThe `getStaticProps` function must now be implemented. So, let's get started. As you can see, the first thing we do is use the Prisma findUnique function with the id retrieved from the query params object to get the data of the requested route. Then, if the corresponding home is found in the database, we return it to the `ListedProducts` React component as a prop. If the requested `products` cannot be found, we return an object to tell Next.js to redirect the user to our app's '`products'` page.\n\n```js\n// pages/products/[id].jsx\nimport Image from \"next/image\";\nimport Layout from \"@/components/Layout\";\nimport { PrismaClient } from \"@prisma/client\";\n// Instantiate Prisma Client\nconst prisma = new PrismaClient();\n\nconst ListedProducts = (product = null) =\u003e {\n  return (\n    \u003cLayout\u003e\n      \u003cdiv className=\"max-w-screen-lg mx-auto\"\u003e\n        \u003cdiv className=\"mt-6 relative aspect-video bg-gray-400 rounded-lg shadow-md overflow-hidden\"\u003e\n          {product?.image ? (\n            \u003cImage\n              src={product.image}\n              alt={product.title}\n              layout=\"fill\"\n              objectFit=\"cover\"\n            /\u003e\n          ) : null}\n        \u003c/div\u003e\n        \u003cdiv className=\"flex flex-col sm:flex-row sm:justify-between sm:space-x-4 space-y-4 pt-10\"\u003e\n          \u003cdiv\u003e\n            \u003ch1 className=\"text-2xl font-semibold truncate\"\u003e\n              {product?.title ?? \"\"}\n            \u003c/h1\u003e\n            \u003col className=\"inline-flex items-center space-x-1 text-info\"\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.status ?? 0} product\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.authenticity ?? 0}% Authentic\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.returnPolicy ?? 0} year return policy\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.warranty ?? 0} year warranty\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n              \u003c/li\u003e\n            \u003c/ol\u003e\n            \u003cp className=\"mt-8 text-lg\"\u003e{product?.description ?? \"\"}\u003c/p\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/Layout\u003e\n  );\n};\n\nexport async function getStaticPaths() {\n  const products = await prisma.product.findMany({\n    select: { id: true },\n  });\n\n  return {\n    paths: products.map((product) =\u003e ({\n      params: { id: product.id },\n    })),\n    fallback: false,\n  };\n}\n\nexport async function getStaticProps({ params }) {\n  const product = await prisma.product.findUnique({\n    where: { id: params.id },\n  });\n\n  if (product) {\n    return {\n      props: JSON.parse(JSON.stringify(product)),\n    };\n  }\n\n  return {\n    redirect: {\n      destination: \"/products\",\n      permanent: false,\n    },\n  };\n}\n\nexport default ListedProducts;\n```\n\nNow re-run the server and head back to the browser and open the application.\n\n![Demo](https://user-images.githubusercontent.com/37651620/160119909-00de7d20-ea1c-448e-b674-b9a752f2308b.png)\n\n![Demo](https://user-images.githubusercontent.com/37651620/160119847-283970a1-f083-4427-ac80-535418353cc7.gif)\n\n## Implementing Incremental Static Generation(ISR)\n\nIf you try to access a page for a new `product` listing in production, you'll get a `404 error page` instead. To see this in action, build your app and run it as you would in production, because `getStaticProps` runs on every request in development. So, we have different behavior in development that differs from what we would see in `production`. To serve a production build of your application, simply fire up the following command, but make sure to stop the server first.\n\n```bash\nyarn build\n```\n\n![yarn build](https://user-images.githubusercontent.com/37651620/160135075-4666a8d8-5f74-471d-9b60-aea22b889896.png)\n\n```bash\nyarn start\n```\n\n![yarn start](https://user-images.githubusercontent.com/37651620/160135296-cfe7f272-b810-4b70-a56b-909dea7d05f5.png)\n\nThe main reason for the `404 page` is that we used Static Generation to define the routes `/products/[id].js`, and we only generated pages for the products that were in our database at the time. In other words, after this build process, none of the products our users create will generate a new page. That is why we have a `404 page` instead, because the page simply does not exist at all.To fix this, we'll need to define a fallback that will allow us to continue building pages lazily at runtime.\n\n```js\n// pages/products/[id].js\nimport Image from \"next/image\";\nimport Layout from \"@/components/Layout\";\nimport { PrismaClient } from \"@prisma/client\";\n// Instantiate Prisma Client\nconst prisma = new PrismaClient();\n\nconst ListedProducts = (product = null) =\u003e {\n  return (\n    \u003cLayout\u003e\n      \u003cdiv className=\"max-w-screen-lg mx-auto\"\u003e\n        \u003cdiv className=\"mt-6 relative aspect-video bg-gray-400 rounded-lg shadow-md overflow-hidden\"\u003e\n          {product?.image ? (\n            \u003cImage\n              src={product.image}\n              alt={product.title}\n              layout=\"fill\"\n              objectFit=\"cover\"\n            /\u003e\n          ) : null}\n        \u003c/div\u003e\n        \u003cdiv className=\"flex flex-col sm:flex-row sm:justify-between sm:space-x-4 space-y-4 pt-10\"\u003e\n          \u003cdiv\u003e\n            \u003ch1 className=\"text-2xl font-semibold truncate\"\u003e\n              {product?.title ?? \"\"}\n            \u003c/h1\u003e\n            \u003col className=\"inline-flex items-center space-x-1 text-info\"\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.status ?? 0} product\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.authenticity ?? 0}% Authentic\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.returnPolicy ?? 0} year return policy\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.warranty ?? 0} year warranty\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n              \u003c/li\u003e\n            \u003c/ol\u003e\n            \u003cp className=\"mt-8 text-lg\"\u003e{product?.description ?? \"\"}\u003c/p\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/Layout\u003e\n  );\n};\n\nexport async function getStaticPaths() {\n  const products = await prisma.product.findMany({\n    select: { id: true },\n  });\n\n  return {\n    paths: products.map((product) =\u003e ({\n      params: { id: product.id },\n    })),\n    // ----- SET to TRUE ------\n    fallback: true,\n  };\n}\n\nexport async function getStaticProps({ params }) {\n  const product = await prisma.product.findUnique({\n    where: { id: params.id },\n  });\n\n  if (product) {\n    return {\n      props: JSON.parse(JSON.stringify(product)),\n    };\n  }\n\n  return {\n    redirect: {\n      destination: \"/products\",\n      permanent: false,\n    },\n  };\n}\n\nexport default ListedProducts;\n```\n\nNow that we've set the `fallback` to `true`, the `404` page will no longer be displayed.\n\n![Fallback](https://user-images.githubusercontent.com/37651620/160141955-14e4651c-bfc4-4dc2-8418-53d5b1639bce.gif)\n\nIt's also possible to detect whether the fallback version of the page is being rendered with the `Next.js router` and, if so, conditionally render something else, such as a loading spinner, while we wait for the props to get loaded.\n\n```js\nconst router = useRouter();\n\nif (router.isFallback) {\n  return (\n    \u003csvg\n      role=\"status\"\n      class=\"mr-2 w-14 h-14 text-gray-200 animate-spin dark:text-gray-600 fill-success\"\n      viewBox=\"0 0 100 101\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    \u003e\n      \u003cpath\n        d=\"M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z\"\n        fill=\"currentColor\"\n      /\u003e\n      \u003cpath\n        d=\"M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z\"\n        fill=\"currentFill\"\n      /\u003e\n    \u003c/svg\u003e\n  );\n}\n```\n\nFinally your `[id].js` code should look something like this.\n\n```jsx\n// pages/products/[id].js\nimport Image from \"next/image\";\nimport Layout from \"@/components/Layout\";\nimport { PrismaClient } from \"@prisma/client\";\nconst prisma = new PrismaClient();\n\nconst ListedProducts = (product = null) =\u003e {\n  const router = useRouter();\n\n  if (router.isFallback) {\n    return (\n      \u003csvg\n        role=\"status\"\n        class=\"mr-2 w-14 h-14 text-gray-200 animate-spin dark:text-gray-600 fill-success\"\n        viewBox=\"0 0 100 101\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      \u003e\n        \u003cpath\n          d=\"M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z\"\n          fill=\"currentColor\"\n        /\u003e\n        \u003cpath\n          d=\"M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z\"\n          fill=\"currentFill\"\n        /\u003e\n      \u003c/svg\u003e\n    );\n  }\n\n  return (\n    \u003cLayout\u003e\n      \u003cdiv className=\"max-w-screen-lg mx-auto\"\u003e\n        \u003cdiv className=\"mt-6 relative aspect-video bg-gray-400 rounded-lg shadow-md overflow-hidden\"\u003e\n          {product?.image ? (\n            \u003cImage\n              src={product.image}\n              alt={product.title}\n              layout=\"fill\"\n              objectFit=\"cover\"\n            /\u003e\n          ) : null}\n        \u003c/div\u003e\n        \u003cdiv className=\"flex flex-col sm:flex-row sm:justify-between sm:space-x-4 space-y-4 pt-10\"\u003e\n          \u003cdiv\u003e\n            \u003ch1 className=\"text-2xl font-semibold truncate\"\u003e\n              {product?.title ?? \"\"}\n            \u003c/h1\u003e\n            \u003col className=\"inline-flex items-center space-x-1 text-info\"\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.status ?? 0} product\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.authenticity ?? 0}% Authentic\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.returnPolicy ?? 0} year return policy\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e - \u003c/span\u003e\n              \u003c/li\u003e\n              \u003cli\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ( \u003c/span\u003e\n                \u003cspan\u003e{product?.warranty ?? 0} year warranty\u003c/span\u003e\n                \u003cspan aria-hidden=\"true\"\u003e ) \u003c/span\u003e\n              \u003c/li\u003e\n            \u003c/ol\u003e\n            \u003cp className=\"mt-8 text-lg\"\u003e{product?.description ?? \"\"}\u003c/p\u003e\n          \u003c/div\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/Layout\u003e\n  );\n};\n\nexport async function getStaticPaths() {\n  const products = await prisma.product.findMany({\n    select: { id: true },\n  });\n\n  return {\n    paths: products.map((product) =\u003e ({\n      params: { id: product.id },\n    })),\n    fallback: false,\n  };\n}\n\nexport async function getStaticProps({ params }) {\n  const product = await prisma.product.findUnique({\n    where: { id: params.id },\n  });\n\n  if (product) {\n    return {\n      props: JSON.parse(JSON.stringify(product)),\n    };\n  }\n\n  return {\n    redirect: {\n      destination: \"/products\",\n      permanent: false,\n    },\n  };\n}\n\nexport default ListedProducts;\n```\n\n## Uploading image in Supabase\n\nWe've created product records up to this point, but without any images because we haven't yet implemented aby media storage. We'll use Supabase Storage, a fantastic service from Supabase, to store and use media files in our project.\n\n### Creating a bucket in supabase\n\nBuckets are distinct containers for files and folders. It is like a `super folders`. Generally you would create distinct buckets for different Security and Access Rules. For example, you might keep all public files in a `public` bucket, and other files that require logged-in access in a `restricted` bucket.\n\nTo create a bucket in Supabase, first navigate to the `storage` section of the dashboard.\n\n![Storage](https://user-images.githubusercontent.com/37651620/160153539-6c318c5c-0dee-4da2-9916-8f8801961afa.gif)\n\nAfter that, select `Create Bucket` button.\n\n![Storage](https://user-images.githubusercontent.com/37651620/160153764-f0f71f25-a3e0-44cf-90ba-f966397a18be.png)\n\nNext, give the bucket a name; for now, we'll call it `supabase-ecommerce`, and remember to make it public and click on that `Create Button` button.\n\n![Storage](https://user-images.githubusercontent.com/37651620/160154077-c0fe180d-2571-4497-8c58-2ae23e89e660.png)\n\n![Storage](https://user-images.githubusercontent.com/37651620/160154148-e235236a-732e-4008-bb2a-6aa8b9ba592f.png)\n\n![Storage](https://user-images.githubusercontent.com/37651620/160154311-35f95ab3-ce34-43af-a0f4-dc8541c4de93.png)\n\n![Storage](https://user-images.githubusercontent.com/37651620/160154342-74a45f3b-eb85-487d-8b58-5a997477aa17.png)\n\n### Manually uploading image on database\n\n- **Step 1**: Head over to the supabase `Storage` and upload the `products` images.\n\n![Boxing](https://user-images.githubusercontent.com/37651620/160235447-6b9deb1c-0bf6-4a4c-97dc-2732dd220cf4.png)\n![Boxing](https://user-images.githubusercontent.com/37651620/160235455-c39b17f7-8d61-4ddb-ac19-2977afe0cbc2.png)\n\n![Action Camera](https://user-images.githubusercontent.com/37651620/160235495-a605f3b4-2505-4284-bfba-14f9c63f533c.png)\n![Action Camera](https://user-images.githubusercontent.com/37651620/160235505-34065298-562a-4d40-9aae-d7543087909d.png)\n\n![Jwellery](https://user-images.githubusercontent.com/37651620/160235537-ebc2cb10-9d1a-4311-b208-b340d0627d1c.png)\n![Jwellery](https://user-images.githubusercontent.com/37651620/160235568-7ae8f3cc-6d6a-48a7-b58d-23be839eec87.png)\n\n- **Step 2**: Select the product image and copy the `image url`\n\n![Boxing](https://user-images.githubusercontent.com/37651620/160235649-554c8f10-64d3-4ad1-a50d-f3e0faa238fb.png)\n\n![Camera](https://user-images.githubusercontent.com/37651620/160235666-52bdedb1-5740-4e1b-a448-9c41c3abd9a7.png)\n\n![Jwellery](https://user-images.githubusercontent.com/37651620/160235704-d22b5981-5c7a-4615-b630-19322c36d803.png)\n\n- **Step 3**: Open up the `Prisma Studio` by typing `npx prisma studio` inside the command line terminal.\n\n![Prisma Studio](https://user-images.githubusercontent.com/37651620/160236083-fd80ccb4-f7ac-4b4a-9576-ad4f754a9917.png)\n\n![Prisma](https://user-images.githubusercontent.com/37651620/160236136-f6b62776-16c9-4cbc-b9d6-f858ea82157a.png)\n\n- **Step 3**: Now, paste all of the image urls you copied in 'Step 2' inside the image row.\n\n![Image URL](https://user-images.githubusercontent.com/37651620/160236233-9ecfde2d-f335-4cb1-a460-d8c2ae3ea79c.png)\n\nGo back to the application and refresh the page now that you've added all of the `image urls`. You may encounter the error shown below.\n\n![Image URL](https://user-images.githubusercontent.com/37651620/160236310-551486ec-1355-4025-8a0f-af47e51b5ddb.png)\n\nCopy the hostname of your file URL and paste it into the `images.domains` config in the `next.config.js` file to fix the error.\n\n```js\nmodule.exports = {\n  reactStrictMode: true,\n  images: {\n    domains: [\"ezkjatblqzjynrebjkpq.supabase.co\"],\n  },\n};\n```\n\nAfter that, restart the server, and you should see images.\n\n![Demo](https://user-images.githubusercontent.com/37651620/160236559-21b8be68-1042-476f-bc9a-8c0aec3b1930.png)\n![Demo](https://user-images.githubusercontent.com/37651620/160236563-161dcd5a-f664-4868-ba20-9cbca4652f43.png)\n![Demo](https://user-images.githubusercontent.com/37651620/160236579-6f80c2de-accd-4d30-a35b-19a990ca5e48.png)\n\n### Security Rules\n\nWe must define some security rules to be able to deal with our image files inside our bucket using the `Supabase API`. So, add the security rules from our `Supabase dashboard`.\n\n- **Step 1**: Head over to the `Storage` section and go to the `Policies` section.\n\n![Security Rules](https://user-images.githubusercontent.com/37651620/160238474-ad39e70f-feff-4bfa-a559-b57cc00a0ac4.png)\n\n- **Step 2**: Create a `New Policy`.\n\n![Security Rules](https://user-images.githubusercontent.com/37651620/160238482-416094e3-5ecf-441f-a8f1-619425257c98.png)\n\n- **Step 3**: Select `Get started quickly`.\n\n![Security Rules](https://user-images.githubusercontent.com/37651620/160238492-b814238e-1723-40ca-bcfc-5b97979974d0.png)\n\n- **Step 4**: Use `Allow access to JPG images in a public folder to anonymous users` this template.\n\n![Security Rules](https://user-images.githubusercontent.com/37651620/160238518-fc9066f6-1031-4f95-90bf-74eda3b7b767.png)\n\n- **Step 5**: Give the `Policy Name` select all the `Operation` and give `bucket_id` and Hit `Review`.\n\n![Security Rules](https://user-images.githubusercontent.com/37651620/160238559-7b2ffdd1-948d-46f9-903a-1e84297d875d.png)\n\n- **Step 6**: `Review` the policy and `save` it.\n\n![Security Rules](https://user-images.githubusercontent.com/37651620/160238568-fd77e15b-4265-499e-a89c-4daaf33a8f07.png)\n\n![Security Rules](https://user-images.githubusercontent.com/37651620/160238578-e542545d-03e9-4072-8466-f3a2eb56c41d.png)\n\n- **Step 8**: Finally you've successfully created a `Storage Policy`.\n\n![Security Rules](https://user-images.githubusercontent.com/37651620/160238595-7be56c15-4a45-4af5-9e02-1b","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpramit-marattha%2Fsupabaseecommerce","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpramit-marattha%2Fsupabaseecommerce","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpramit-marattha%2Fsupabaseecommerce/lists"}