Ultimate Guide: Integrating Shopify with Next.js for a Blazing-Fast E-commerce Experience
by Jonathan M. James, Founder & CEO
Creating a modern, performance-centric e-commerce platform involves marrying robust e-commerce functionality with front-end excellence. Here, we combine Next.js, Shopify, and TailwindCSS to achieve this union. Let's dive in!
Why Next.js with Shopify?
Next.js offers server-side rendering, route pre-fetching, and a developer-friendly experience. Integrating it with Shopify's robust backend ensures a powerful e-commerce engine.
Sprucing Up with TailwindCSS
TailwindCSS is a utility-first CSS framework, perfect for rapidly crafting modern UI without leaving your HTML.
Prerequisites:
-
Basic understanding of React and Next.js,
-
Created Shopify Partner account.
- If you aren't already a Shopify Partner, you'll need to become one. Go to the Shopify Partners page and click on the "Join now" button.
- Fill out the form with your details. Once you complete the sign-up process, you'll get access to the Shopify Partner Dashboard.
- Log in to your Shopify Partner Dashboard. This is where you'll manage all your development stores, affiliate referrals, and client accounts.
-
Created a Shopify Development Store.
- In your Partner Dashboard, click on the
Development stores
tab on the left sidebar.
- In your Partner Dashboard, click on the
-
Click on the
Create store
button. -
Fill in the required information:
-
Store name: Choose a unique name for your development store.
-
Password: Set a password for your store's admin.
-
Store purpose: Choose the most fitting purpose. For instance, if you're building a store to showcase your theme or app, select "Demo shop for an app or theme I'm selling".
-
Store type: You can choose between a
Development store
(which is free but has limitations) and aManaged store
(which can become a paid store). -
After filling in the information, click on the
Save
button.
-
Setting the Stage: Initial Setup
- Initialize Next.js:
pnpm dlx create-next-app next-shopify-tailwind cd next-shopify-tailwind
Note: We used pnpm for our package manager but you may used npm, or yarn.
Building Our Storefront
- First we will need to replace the example code that comes with the Next.js app creation. To do this we will open
app/src/page.js
then replace the Next.js example code with the storefront designed with tailwindcss code provided below.
'use client'
import { Fragment, useState } from 'react'
import { Dialog, Popover, Tab, Transition } from '@headlessui/react'
import {
Bars3Icon,
MagnifyingGlassIcon,
QuestionMarkCircleIcon,
ShoppingBagIcon,
XMarkIcon,
} from '@heroicons/react/24/outline'
import { ChevronDownIcon } from '@heroicons/react/20/solid'
const currencies = ['CAD', 'USD', 'AUD', 'EUR', 'GBP']
const navigation = {
categories: [
{
name: 'Women',
featured: [
{
name: 'New Arrivals',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-category-01.jpg',
imageAlt: 'Models sitting back to back, wearing Basic Tee in black and bone.',
},
{
name: 'Basic Tees',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-category-02.jpg',
imageAlt: 'Close up of Basic Tee fall bundle with off-white, ochre, olive, and black tees.',
},
{
name: 'Accessories',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-category-03.jpg',
imageAlt: 'Model wearing minimalist watch with black wristband and white watch face.',
},
{
name: 'Carry',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-category-04.jpg',
imageAlt: 'Model opening tan leather long wallet with credit card pockets and cash pouch.',
},
],
},
{
name: 'Men',
featured: [
{
name: 'New Arrivals',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-01-men-category-01.jpg',
imageAlt: 'Hats and sweaters on wood shelves next to various colors of t-shirts on hangers.',
},
{
name: 'Basic Tees',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-01-men-category-02.jpg',
imageAlt: 'Model wearing light heather gray t-shirt.',
},
{
name: 'Accessories',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-01-men-category-03.jpg',
imageAlt:
'Grey 6-panel baseball hat with black brim, black mountain graphic on front, and light heather gray body.',
},
{
name: 'Carry',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-01-men-category-04.jpg',
imageAlt: 'Model putting folded cash into slim card holder olive leather wallet with hand stitching.',
},
],
},
],
pages: [
{ name: 'Company', href: '#' },
{ name: 'Stores', href: '#' },
],
}
const categories = [
{
name: 'New Arrivals',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-category-01.jpg',
},
{
name: 'Productivity',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-category-02.jpg',
},
{
name: 'Workspace',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-category-04.jpg',
},
{
name: 'Accessories',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-category-05.jpg',
},
{ name: 'Sale', href: '#', imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-category-03.jpg' },
]
const collections = [
{
name: 'Handcrafted Collection',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-collection-01.jpg',
imageAlt: 'Brown leather key ring with brass metal loops and rivets on wood table.',
description: 'Keep your phone, keys, and wallet together, so you can lose everything at once.',
},
{
name: 'Organized Desk Collection',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-collection-02.jpg',
imageAlt: 'Natural leather mouse pad on white desk next to porcelain mug and keyboard.',
description: 'The rest of the house will still be a mess, but your desk will look great.',
},
{
name: 'Focus Collection',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-collection-03.jpg',
imageAlt: 'Person placing task list card into walnut card holder next to felt carrying case on leather desk pad.',
description: 'Be more productive than enterprise project managers with a single piece of paper.',
},
]
const footerNavigation = {
shop: [
{ name: 'Bags', href: '#' },
{ name: 'Tees', href: '#' },
{ name: 'Objects', href: '#' },
{ name: 'Home Goods', href: '#' },
{ name: 'Accessories', href: '#' },
],
company: [
{ name: 'Who we are', href: '#' },
{ name: 'Sustainability', href: '#' },
{ name: 'Press', href: '#' },
{ name: 'Careers', href: '#' },
{ name: 'Terms & Conditions', href: '#' },
{ name: 'Privacy', href: '#' },
],
account: [
{ name: 'Manage Account', href: '#' },
{ name: 'Returns & Exchanges', href: '#' },
{ name: 'Redeem a Gift Card', href: '#' },
],
connect: [
{ name: 'Contact Us', href: '#' },
{ name: 'Twitter', href: '#' },
{ name: 'Instagram', href: '#' },
{ name: 'Pinterest', href: '#' },
],
}
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export default function Example() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
return (
<div className="bg-white">
{/* Mobile menu */}
<Transition.Root show={mobileMenuOpen} as={Fragment}>
<Dialog as="div" className="relative z-40 lg:hidden" onClose={setMobileMenuOpen}>
<Transition.Child
as={Fragment}
enter="transition-opacity ease-linear duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity ease-linear duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black bg-opacity-25" />
</Transition.Child>
<div className="fixed inset-0 z-40 flex">
<Transition.Child
as={Fragment}
enter="transition ease-in-out duration-300 transform"
enterFrom="-translate-x-full"
enterTo="translate-x-0"
leave="transition ease-in-out duration-300 transform"
leaveFrom="translate-x-0"
leaveTo="-translate-x-full"
>
<Dialog.Panel className="relative flex w-full max-w-xs flex-col overflow-y-auto bg-white pb-12 shadow-xl">
<div className="flex px-4 pb-2 pt-5">
<button
type="button"
className="-m-2 inline-flex items-center justify-center rounded-md p-2 text-gray-400"
onClick={() => setMobileMenuOpen(false)}
>
<span className="sr-only">Close menu</span>
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
{/* Links */}
<Tab.Group as="div" className="mt-2">
<div className="border-b border-gray-200">
<Tab.List className="-mb-px flex space-x-8 px-4">
{navigation.categories.map((category) => (
<Tab
key={category.name}
className={({ selected }) =>
classNames(
selected ? 'border-indigo-600 text-indigo-600' : 'border-transparent text-gray-900',
'flex-1 whitespace-nowrap border-b-2 px-1 py-4 text-base font-medium'
)
}
>
{category.name}
</Tab>
))}
</Tab.List>
</div>
<Tab.Panels as={Fragment}>
{navigation.categories.map((category) => (
<Tab.Panel key={category.name} className="space-y-12 px-4 py-6">
<div className="grid grid-cols-2 gap-x-4 gap-y-10">
{category.featured.map((item) => (
<div key={item.name} className="group relative">
<div className="aspect-h-1 aspect-w-1 overflow-hidden rounded-md bg-gray-100 group-hover:opacity-75">
<img src={item.imageSrc} alt={item.imageAlt} className="object-cover object-center" />
</div>
<a href={item.href} className="mt-6 block text-sm font-medium text-gray-900">
<span className="absolute inset-0 z-10" aria-hidden="true" />
{item.name}
</a>
<p aria-hidden="true" className="mt-1 text-sm text-gray-500">
Shop now
</p>
</div>
))}
</div>
</Tab.Panel>
))}
</Tab.Panels>
</Tab.Group>
<div className="space-y-6 border-t border-gray-200 px-4 py-6">
{navigation.pages.map((page) => (
<div key={page.name} className="flow-root">
<a href={page.href} className="-m-2 block p-2 font-medium text-gray-900">
{page.name}
</a>
</div>
))}
</div>
<div className="space-y-6 border-t border-gray-200 px-4 py-6">
<div className="flow-root">
<a href="#" className="-m-2 block p-2 font-medium text-gray-900">
Create an account
</a>
</div>
<div className="flow-root">
<a href="#" className="-m-2 block p-2 font-medium text-gray-900">
Sign in
</a>
</div>
</div>
<div className="space-y-6 border-t border-gray-200 px-4 py-6">
{/* Currency selector */}
<form>
<div className="inline-block">
<label htmlFor="mobile-currency" className="sr-only">
Currency
</label>
<div className="group relative -ml-2 rounded-md border-transparent focus-within:ring-2 focus-within:ring-white">
<select
id="mobile-currency"
name="currency"
className="flex items-center rounded-md border-transparent bg-none py-0.5 pl-2 pr-5 text-sm font-medium text-gray-700 focus:border-transparent focus:outline-none focus:ring-0 group-hover:text-gray-800"
>
{currencies.map((currency) => (
<option key={currency}>{currency}</option>
))}
</select>
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center">
<ChevronDownIcon className="h-5 w-5 text-gray-500" aria-hidden="true" />
</div>
</div>
</div>
</form>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</Dialog>
</Transition.Root>
{/* Hero section */}
<div className="relative bg-gray-900">
{/* Decorative image and overlay */}
<div aria-hidden="true" className="absolute inset-0 overflow-hidden">
<img
src="https://tailwindui.com/img/ecommerce-images/home-page-01-hero-full-width.jpg"
alt=""
className="h-full w-full object-cover object-center"
/>
</div>
<div aria-hidden="true" className="absolute inset-0 bg-gray-900 opacity-50" />
{/* Navigation */}
<header className="relative z-10">
<nav aria-label="Top">
{/* Top navigation */}
<div className="bg-gray-900">
<div className="mx-auto flex h-10 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
{/* Currency selector */}
<form>
<div>
<label htmlFor="desktop-currency" className="sr-only">
Currency
</label>
<div className="group relative -ml-2 rounded-md border-transparent bg-gray-900 focus-within:ring-2 focus-within:ring-white">
<select
id="desktop-currency"
name="currency"
className="flex items-center rounded-md border-transparent bg-gray-900 bg-none py-0.5 pl-2 pr-5 text-sm font-medium text-white focus:border-transparent focus:outline-none focus:ring-0 group-hover:text-gray-100"
>
{currencies.map((currency) => (
<option key={currency}>{currency}</option>
))}
</select>
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center">
<ChevronDownIcon className="h-5 w-5 text-gray-300" aria-hidden="true" />
</div>
</div>
</div>
</form>
<div className="flex items-center space-x-6">
<a href="#" className="text-sm font-medium text-white hover:text-gray-100">
Sign in
</a>
<a href="#" className="text-sm font-medium text-white hover:text-gray-100">
Create an account
</a>
</div>
</div>
</div>
{/* Secondary navigation */}
<div className="bg-white bg-opacity-10 backdrop-blur-md backdrop-filter">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div>
<div className="flex h-16 items-center justify-between">
{/* Logo (lg+) */}
<div className="hidden lg:flex lg:flex-1 lg:items-center">
<a href="#">
<span className="sr-only">Your Company</span>
<img
className="h-8 w-auto"
src="https://storage.googleapis.com/lullipop-image-dump/Motstudio_logo.svg"
alt=""
/>
</a>
</div>
<div className="hidden h-full lg:flex">
{/* Flyout menus */}
<Popover.Group className="inset-x-0 bottom-0 px-4">
<div className="flex h-full justify-center space-x-8">
{navigation.categories.map((category) => (
<Popover key={category.name} className="flex">
{({ open }) => (
<>
<div className="relative flex">
<Popover.Button className="relative z-10 flex items-center justify-center text-sm font-medium text-white transition-colors duration-200 ease-out">
{category.name}
<span
className={classNames(
open ? 'bg-white' : '',
'absolute inset-x-0 -bottom-px h-0.5 transition duration-200 ease-out'
)}
aria-hidden="true"
/>
</Popover.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition ease-in duration-150"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Popover.Panel className="absolute inset-x-0 top-full text-sm text-gray-500">
{/* Presentational element used to render the bottom shadow, if we put the shadow on the actual panel it pokes out the top, so we use this shorter element to hide the top of the shadow */}
<div className="absolute inset-0 top-1/2 bg-white shadow" aria-hidden="true" />
<div className="relative bg-white">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-4 gap-x-8 gap-y-10 py-16">
{category.featured.map((item) => (
<div key={item.name} className="group relative">
<div className="aspect-h-1 aspect-w-1 overflow-hidden rounded-md bg-gray-100 group-hover:opacity-75">
<img
src={item.imageSrc}
alt={item.imageAlt}
className="object-cover object-center"
/>
</div>
<a href={item.href} className="mt-4 block font-medium text-gray-900">
<span className="absolute inset-0 z-10" aria-hidden="true" />
{item.name}
</a>
<p aria-hidden="true" className="mt-1">
Shop now
</p>
</div>
))}
</div>
</div>
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
))}
{navigation.pages.map((page) => (
<a
key={page.name}
href={page.href}
className="flex items-center text-sm font-medium text-white"
>
{page.name}
</a>
))}
</div>
</Popover.Group>
</div>
{/* Mobile menu and search (lg-) */}
<div className="flex flex-1 items-center lg:hidden">
<button type="button" className="-ml-2 p-2 text-white" onClick={() => setMobileMenuOpen(true)}>
<span className="sr-only">Open menu</span>
<Bars3Icon className="h-6 w-6" aria-hidden="true" />
</button>
{/* Search */}
<a href="#" className="ml-2 p-2 text-white">
<span className="sr-only">Search</span>
<MagnifyingGlassIcon className="h-6 w-6" aria-hidden="true" />
</a>
</div>
{/* Logo (lg-) */}
<a href="#" className="lg:hidden">
<span className="sr-only">Your Company</span>
<img src="https://storage.googleapis.com/lullipop-image-dump/Motstudio_logo.svg" alt="" className="h-8 w-auto fill-white" />
</a>
<div className="flex flex-1 items-center justify-end">
<a href="#" className="hidden text-sm font-medium text-white lg:block">
Search
</a>
<div className="flex items-center lg:ml-8">
{/* Help */}
<a href="#" className="p-2 text-white lg:hidden">
<span className="sr-only">Help</span>
<QuestionMarkCircleIcon className="h-6 w-6" aria-hidden="true" />
</a>
<a href="#" className="hidden text-sm font-medium text-white lg:block">
Help
</a>
{/* Cart */}
<div className="ml-4 flow-root lg:ml-8">
<a href="#" className="group -m-2 flex items-center p-2">
<ShoppingBagIcon className="h-6 w-6 flex-shrink-0 text-white" aria-hidden="true" />
<span className="ml-2 text-sm font-medium text-white">0</span>
<span className="sr-only">items in cart, view bag</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</nav>
</header>
<div className="relative mx-auto flex max-w-3xl flex-col items-center px-6 py-32 text-center sm:py-64 lg:px-0">
<h1 className="text-4xl font-bold tracking-tight text-white lg:text-6xl">New arrivals are here</h1>
<p className="mt-4 text-xl text-white">
The new arrivals have, well, newly arrived. Check out the latest options from our summer small-batch release
while they're still in stock.
</p>
<a
href="#"
className="mt-8 inline-block rounded-md border border-transparent bg-white px-8 py-3 text-base font-medium text-gray-900 hover:bg-gray-100"
>
Shop New Arrivals
</a>
</div>
</div>
<main>
{/* Category section */}
<section aria-labelledby="category-heading" className="pt-24 sm:pt-32 xl:mx-auto xl:max-w-7xl xl:px-8">
<div className="px-4 sm:flex sm:items-center sm:justify-between sm:px-6 lg:px-8 xl:px-0">
<h2 id="category-heading" className="text-2xl font-bold tracking-tight text-gray-900">
Shop by Category
</h2>
<a href="#" className="hidden text-sm font-semibold text-indigo-600 hover:text-indigo-500 sm:block">
Browse all categories
<span aria-hidden="true"> →</span>
</a>
</div>
<div className="mt-4 flow-root">
<div className="-my-2">
<div className="relative box-content h-80 overflow-x-auto py-2 xl:overflow-visible">
<div className="absolute flex space-x-8 px-4 sm:px-6 lg:px-8 xl:relative xl:grid xl:grid-cols-5 xl:gap-x-8 xl:space-x-0 xl:px-0">
{categories.map((category) => (
<a
key={category.name}
href={category.href}
className="relative flex h-80 w-56 flex-col overflow-hidden rounded-lg p-6 hover:opacity-75 xl:w-auto"
>
<span aria-hidden="true" className="absolute inset-0">
<img src={category.imageSrc} alt="" className="h-full w-full object-cover object-center" />
</span>
<span
aria-hidden="true"
className="absolute inset-x-0 bottom-0 h-2/3 bg-gradient-to-t from-gray-800 opacity-50"
/>
<span className="relative mt-auto text-center text-xl font-bold text-white">{category.name}</span>
</a>
))}
</div>
</div>
</div>
</div>
<div className="mt-6 px-4 sm:hidden">
<a href="#" className="block text-sm font-semibold text-indigo-600 hover:text-indigo-500">
Browse all categories
<span aria-hidden="true"> →</span>
</a>
</div>
</section>
{/* Featured section */}
<section
aria-labelledby="social-impact-heading"
className="mx-auto max-w-7xl px-4 pt-24 sm:px-6 sm:pt-32 lg:px-8"
>
<div className="relative overflow-hidden rounded-lg">
<div className="absolute inset-0">
<img
src="https://tailwindui.com/img/ecommerce-images/home-page-01-feature-section-01.jpg"
alt=""
className="h-full w-full object-cover object-center"
/>
</div>
<div className="relative bg-gray-900 bg-opacity-75 px-6 py-32 sm:px-12 sm:py-40 lg:px-16">
<div className="relative mx-auto flex max-w-3xl flex-col items-center text-center">
<h2 id="social-impact-heading" className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
<span className="block sm:inline">Level up</span>
<span className="block sm:inline">your desk</span>
</h2>
<p className="mt-3 text-xl text-white">
Make your desk beautiful and organized. Post a picture to social media and watch it get more likes
than life-changing announcements. Reflect on the shallow nature of existence. At least you have a
really nice desk setup.
</p>
<a
href="#"
className="mt-8 block w-full rounded-md border border-transparent bg-white px-8 py-3 text-base font-medium text-gray-900 hover:bg-gray-100 sm:w-auto"
>
Shop Workspace
</a>
</div>
</div>
</div>
</section>
{/* Collection section */}
<section
aria-labelledby="collection-heading"
className="mx-auto max-w-xl px-4 pt-24 sm:px-6 sm:pt-32 lg:max-w-7xl lg:px-8"
>
<h2 id="collection-heading" className="text-2xl font-bold tracking-tight text-gray-900">
Shop by Collection
</h2>
<p className="mt-4 text-base text-gray-500">
Each season, we collaborate with world-class designers to create a collection inspired by the natural world.
</p>
<div className="mt-10 space-y-12 lg:grid lg:grid-cols-3 lg:gap-x-8 lg:space-y-0">
{collections.map((collection) => (
<a key={collection.name} href={collection.href} className="group block">
<div
aria-hidden="true"
className="aspect-h-2 aspect-w-3 overflow-hidden rounded-lg lg:aspect-h-6 lg:aspect-w-5 group-hover:opacity-75"
>
<img
src={collection.imageSrc}
alt={collection.imageAlt}
className="h-full w-full object-cover object-center"
/>
</div>
<h3 className="mt-4 text-base font-semibold text-gray-900">{collection.name}</h3>
<p className="mt-2 text-sm text-gray-500">{collection.description}</p>
</a>
))}
</div>
</section>
{/* Featured section */}
<section aria-labelledby="comfort-heading" className="mx-auto max-w-7xl px-4 py-24 sm:px-6 sm:py-32 lg:px-8">
<div className="relative overflow-hidden rounded-lg">
<div className="absolute inset-0">
<img
src="https://tailwindui.com/img/ecommerce-images/home-page-01-feature-section-02.jpg"
alt=""
className="h-full w-full object-cover object-center"
/>
</div>
<div className="relative bg-gray-900 bg-opacity-75 px-6 py-32 sm:px-12 sm:py-40 lg:px-16">
<div className="relative mx-auto flex max-w-3xl flex-col items-center text-center">
<h2 id="comfort-heading" className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
Simple productivity
</h2>
<p className="mt-3 text-xl text-white">
Endless tasks, limited hours, a single piece of paper. Not really a haiku, but we're doing our best
here. No kanban boards, burndown charts, or tangled flowcharts with our Focus system. Just the
undeniable urge to fill empty circles.
</p>
<a
href="#"
className="mt-8 block w-full rounded-md border border-transparent bg-white px-8 py-3 text-base font-medium text-gray-900 hover:bg-gray-100 sm:w-auto"
>
Shop Focus
</a>
</div>
</div>
</div>
</section>
</main>
<footer aria-labelledby="footer-heading" className="bg-gray-900">
<h2 id="footer-heading" className="sr-only">
Footer
</h2>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="py-20 xl:grid xl:grid-cols-3 xl:gap-8">
<div className="grid grid-cols-2 gap-8 xl:col-span-2">
<div className="space-y-12 md:grid md:grid-cols-2 md:gap-8 md:space-y-0">
<div>
<h3 className="text-sm font-medium text-white">Shop</h3>
<ul role="list" className="mt-6 space-y-6">
{footerNavigation.shop.map((item) => (
<li key={item.name} className="text-sm">
<a href={item.href} className="text-gray-300 hover:text-white">
{item.name}
</a>
</li>
))}
</ul>
</div>
<div>
<h3 className="text-sm font-medium text-white">Company</h3>
<ul role="list" className="mt-6 space-y-6">
{footerNavigation.company.map((item) => (
<li key={item.name} className="text-sm">
<a href={item.href} className="text-gray-300 hover:text-white">
{item.name}
</a>
</li>
))}
</ul>
</div>
</div>
<div className="space-y-12 md:grid md:grid-cols-2 md:gap-8 md:space-y-0">
<div>
<h3 className="text-sm font-medium text-white">Account</h3>
<ul role="list" className="mt-6 space-y-6">
{footerNavigation.account.map((item) => (
<li key={item.name} className="text-sm">
<a href={item.href} className="text-gray-300 hover:text-white">
{item.name}
</a>
</li>
))}
</ul>
</div>
<div>
<h3 className="text-sm font-medium text-white">Connect</h3>
<ul role="list" className="mt-6 space-y-6">
{footerNavigation.connect.map((item) => (
<li key={item.name} className="text-sm">
<a href={item.href} className="text-gray-300 hover:text-white">
{item.name}
</a>
</li>
))}
</ul>
</div>
</div>
</div>
<div className="mt-12 md:mt-16 xl:mt-0">
<h3 className="text-sm font-medium text-white">Sign up for our newsletter</h3>
<p className="mt-6 text-sm text-gray-300">The latest deals and savings, sent to your inbox weekly.</p>
<form className="mt-2 flex sm:max-w-md">
<label htmlFor="email-address" className="sr-only">
Email address
</label>
<input
id="email-address"
type="text"
autoComplete="email"
required
className="w-full min-w-0 appearance-none rounded-md border border-white bg-white px-4 py-2 text-base text-gray-900 placeholder-gray-500 shadow-sm focus:border-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-900"
/>
<div className="ml-4 flex-shrink-0">
<button
type="submit"
className="flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-gray-900"
>
Sign up
</button>
</div>
</form>
</div>
</div>
<div className="border-t border-gray-800 py-10">
<p className="text-sm text-gray-400">Copyright © 2021 Your Company, Inc.</p>
</div>
</div>
</footer>
</div>
)
}
Now we should have a storefront home page that looks like this.
Setting Up the Product Page
With the release of Next.js 13, routing has been enhanced with the introduction of the app router. To make sure our e-commerce store has a dedicated product page, follow these steps:
- Navigate to the
app
directory in your project root. - Create a new folder within it and name it
products
. - Inside the
products
folder, create a new file namedpage.jsx
.
This will serve as the foundation for our product display page. In the following sections, we'll delve into the code structure and functionalities required for this page.
Adding the Product Page Code
Now, let's dive into the code structure and functionalities required for our product page. Open the page.jsx
file you just created and paste the following code:
'use client'
import { Fragment, useState } from 'react'
import { Dialog, Popover, Tab, Transition } from '@headlessui/react'
import {
Bars3Icon,
MagnifyingGlassIcon,
QuestionMarkCircleIcon,
ShoppingBagIcon,
XMarkIcon,
} from '@heroicons/react/24/outline'
import { ChevronDownIcon } from '@heroicons/react/20/solid'
const currencies = ['CAD', 'USD', 'AUD', 'EUR', 'GBP']
const navigation = {
categories: [
{
name: 'Women',
featured: [
{
name: 'New Arrivals',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-category-01.jpg',
imageAlt: 'Models sitting back to back, wearing Basic Tee in black and bone.',
},
{
name: 'Basic Tees',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-category-02.jpg',
imageAlt: 'Close up of Basic Tee fall bundle with off-white, ochre, olive, and black tees.',
},
{
name: 'Accessories',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-category-03.jpg',
imageAlt: 'Model wearing minimalist watch with black wristband and white watch face.',
},
{
name: 'Carry',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-category-04.jpg',
imageAlt: 'Model opening tan leather long wallet with credit card pockets and cash pouch.',
},
],
},
{
name: 'Men',
featured: [
{
name: 'New Arrivals',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-01-men-category-01.jpg',
imageAlt: 'Hats and sweaters on wood shelves next to various colors of t-shirts on hangers.',
},
{
name: 'Basic Tees',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-01-men-category-02.jpg',
imageAlt: 'Model wearing light heather gray t-shirt.',
},
{
name: 'Accessories',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-01-men-category-03.jpg',
imageAlt:
'Grey 6-panel baseball hat with black brim, black mountain graphic on front, and light heather gray body.',
},
{
name: 'Carry',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/mega-menu-01-men-category-04.jpg',
imageAlt: 'Model putting folded cash into slim card holder olive leather wallet with hand stitching.',
},
],
},
],
pages: [
{ name: 'Company', href: '#' },
{ name: 'Stores', href: '#' },
],
}
const categories = [
{
name: 'New Arrivals',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-category-01.jpg',
},
{
name: 'Productivity',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-category-02.jpg',
},
{
name: 'Workspace',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-category-04.jpg',
},
{
name: 'Accessories',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-category-05.jpg',
},
{ name: 'Sale', href: '#', imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-category-03.jpg' },
]
const collections = [
{
name: 'Handcrafted Collection',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-collection-01.jpg',
imageAlt: 'Brown leather key ring with brass metal loops and rivets on wood table.',
description: 'Keep your phone, keys, and wallet together, so you can lose everything at once.',
},
{
name: 'Organized Desk Collection',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-collection-02.jpg',
imageAlt: 'Natural leather mouse pad on white desk next to porcelain mug and keyboard.',
description: 'The rest of the house will still be a mess, but your desk will look great.',
},
{
name: 'Focus Collection',
href: '#',
imageSrc: 'https://tailwindui.com/img/ecommerce-images/home-page-01-collection-03.jpg',
imageAlt: 'Person placing task list card into walnut card holder next to felt carrying case on leather desk pad.',
description: 'Be more productive than enterprise project managers with a single piece of paper.',
},
]
const footerNavigation = {
shop: [
{ name: 'Bags', href: '#' },
{ name: 'Tees', href: '#' },
{ name: 'Objects', href: '#' },
{ name: 'Home Goods', href: '#' },
{ name: 'Accessories', href: '#' },
],
company: [
{ name: 'Who we are', href: '#' },
{ name: 'Sustainability', href: '#' },
{ name: 'Press', href: '#' },
{ name: 'Careers', href: '#' },
{ name: 'Terms & Conditions', href: '#' },
{ name: 'Privacy', href: '#' },
],
account: [
{ name: 'Manage Account', href: '#' },
{ name: 'Returns & Exchanges', href: '#' },
{ name: 'Redeem a Gift Card', href: '#' },
],
connect: [
{ name: 'Contact Us', href: '#' },
{ name: 'Twitter', href: '#' },
{ name: 'Instagram', href: '#' },
{ name: 'Pinterest', href: '#' },
],
}
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export default function Example() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
return (
<div className="bg-white">
{/* Mobile menu */}
<Transition.Root show={mobileMenuOpen} as={Fragment}>
<Dialog as="div" className="relative z-40 lg:hidden" onClose={setMobileMenuOpen}>
<Transition.Child
as={Fragment}
enter="transition-opacity ease-linear duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity ease-linear duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black bg-opacity-25" />
</Transition.Child>
<div className="fixed inset-0 z-40 flex">
<Transition.Child
as={Fragment}
enter="transition ease-in-out duration-300 transform"
enterFrom="-translate-x-full"
enterTo="translate-x-0"
leave="transition ease-in-out duration-300 transform"
leaveFrom="translate-x-0"
leaveTo="-translate-x-full"
>
<Dialog.Panel className="relative flex w-full max-w-xs flex-col overflow-y-auto bg-white pb-12 shadow-xl">
<div className="flex px-4 pb-2 pt-5">
<button
type="button"
className="-m-2 inline-flex items-center justify-center rounded-md p-2 text-gray-400"
onClick={() => setMobileMenuOpen(false)}
>
<span className="sr-only">Close menu</span>
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
{/* Links */}
<Tab.Group as="div" className="mt-2">
<div className="border-b border-gray-200">
<Tab.List className="-mb-px flex space-x-8 px-4">
{navigation.categories.map((category) => (
<Tab
key={category.name}
className={({ selected }) =>
classNames(
selected ? 'border-indigo-600 text-indigo-600' : 'border-transparent text-gray-900',
'flex-1 whitespace-nowrap border-b-2 px-1 py-4 text-base font-medium'
)
}
>
{category.name}
</Tab>
))}
</Tab.List>
</div>
<Tab.Panels as={Fragment}>
{navigation.categories.map((category) => (
<Tab.Panel key={category.name} className="space-y-12 px-4 py-6">
<div className="grid grid-cols-2 gap-x-4 gap-y-10">
{category.featured.map((item) => (
<div key={item.name} className="group relative">
<div className="aspect-h-1 aspect-w-1 overflow-hidden rounded-md bg-gray-100 group-hover:opacity-75">
<img src={item.imageSrc} alt={item.imageAlt} className="object-cover object-center" />
</div>
<a href={item.href} className="mt-6 block text-sm font-medium text-gray-900">
<span className="absolute inset-0 z-10" aria-hidden="true" />
{item.name}
</a>
<p aria-hidden="true" className="mt-1 text-sm text-gray-500">
Shop now
</p>
</div>
))}
</div>
</Tab.Panel>
))}
</Tab.Panels>
</Tab.Group>
<div className="space-y-6 border-t border-gray-200 px-4 py-6">
{navigation.pages.map((page) => (
<div key={page.name} className="flow-root">
<a href={page.href} className="-m-2 block p-2 font-medium text-gray-900">
{page.name}
</a>
</div>
))}
</div>
<div className="space-y-6 border-t border-gray-200 px-4 py-6">
<div className="flow-root">
<a href="#" className="-m-2 block p-2 font-medium text-gray-900">
Create an account
</a>
</div>
<div className="flow-root">
<a href="#" className="-m-2 block p-2 font-medium text-gray-900">
Sign in
</a>
</div>
</div>
<div className="space-y-6 border-t border-gray-200 px-4 py-6">
{/* Currency selector */}
<form>
<div className="inline-block">
<label htmlFor="mobile-currency" className="sr-only">
Currency
</label>
<div className="group relative -ml-2 rounded-md border-transparent focus-within:ring-2 focus-within:ring-white">
<select
id="mobile-currency"
name="currency"
className="flex items-center rounded-md border-transparent bg-none py-0.5 pl-2 pr-5 text-sm font-medium text-gray-700 focus:border-transparent focus:outline-none focus:ring-0 group-hover:text-gray-800"
>
{currencies.map((currency) => (
<option key={currency}>{currency}</option>
))}
</select>
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center">
<ChevronDownIcon className="h-5 w-5 text-gray-500" aria-hidden="true" />
</div>
</div>
</div>
</form>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</Dialog>
</Transition.Root>
{/* Hero section */}
<div className="relative bg-gray-900">
{/* Decorative image and overlay */}
<div aria-hidden="true" className="absolute inset-0 overflow-hidden">
<img
src="https://tailwindui.com/img/ecommerce-images/home-page-01-hero-full-width.jpg"
alt=""
className="h-full w-full object-cover object-center"
/>
</div>
<div aria-hidden="true" className="absolute inset-0 bg-gray-900 opacity-50" />
{/* Navigation */}
<header className="relative z-10">
<nav aria-label="Top">
{/* Top navigation */}
<div className="bg-gray-900">
<div className="mx-auto flex h-10 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
{/* Currency selector */}
<form>
<div>
<label htmlFor="desktop-currency" className="sr-only">
Currency
</label>
<div className="group relative -ml-2 rounded-md border-transparent bg-gray-900 focus-within:ring-2 focus-within:ring-white">
<select
id="desktop-currency"
name="currency"
className="flex items-center rounded-md border-transparent bg-gray-900 bg-none py-0.5 pl-2 pr-5 text-sm font-medium text-white focus:border-transparent focus:outline-none focus:ring-0 group-hover:text-gray-100"
>
{currencies.map((currency) => (
<option key={currency}>{currency}</option>
))}
</select>
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center">
<ChevronDownIcon className="h-5 w-5 text-gray-300" aria-hidden="true" />
</div>
</div>
</div>
</form>
<div className="flex items-center space-x-6">
<a href="#" className="text-sm font-medium text-white hover:text-gray-100">
Sign in
</a>
<a href="#" className="text-sm font-medium text-white hover:text-gray-100">
Create an account
</a>
</div>
</div>
</div>
{/* Secondary navigation */}
<div className="bg-white bg-opacity-10 backdrop-blur-md backdrop-filter">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div>
<div className="flex h-16 items-center justify-between">
{/* Logo (lg+) */}
<div className="hidden lg:flex lg:flex-1 lg:items-center">
<a href="#">
<span className="sr-only">Your Company</span>
<img
className="h-8 w-auto"
src="https://storage.googleapis.com/lullipop-image-dump/Motstudio_logo.svg"
alt=""
/>
</a>
</div>
<div className="hidden h-full lg:flex">
{/* Flyout menus */}
<Popover.Group className="inset-x-0 bottom-0 px-4">
<div className="flex h-full justify-center space-x-8">
{navigation.categories.map((category) => (
<Popover key={category.name} className="flex">
{({ open }) => (
<>
<div className="relative flex">
<Popover.Button className="relative z-10 flex items-center justify-center text-sm font-medium text-white transition-colors duration-200 ease-out">
{category.name}
<span
className={classNames(
open ? 'bg-white' : '',
'absolute inset-x-0 -bottom-px h-0.5 transition duration-200 ease-out'
)}
aria-hidden="true"
/>
</Popover.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition ease-in duration-150"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Popover.Panel className="absolute inset-x-0 top-full text-sm text-gray-500">
{/* Presentational element used to render the bottom shadow, if we put the shadow on the actual panel it pokes out the top, so we use this shorter element to hide the top of the shadow */}
<div className="absolute inset-0 top-1/2 bg-white shadow" aria-hidden="true" />
<div className="relative bg-white">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-4 gap-x-8 gap-y-10 py-16">
{category.featured.map((item) => (
<div key={item.name} className="group relative">
<div className="aspect-h-1 aspect-w-1 overflow-hidden rounded-md bg-gray-100 group-hover:opacity-75">
<img
src={item.imageSrc}
alt={item.imageAlt}
className="object-cover object-center"
/>
</div>
<a href={item.href} className="mt-4 block font-medium text-gray-900">
<span className="absolute inset-0 z-10" aria-hidden="true" />
{item.name}
</a>
<p aria-hidden="true" className="mt-1">
Shop now
</p>
</div>
))}
</div>
</div>
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
))}
{navigation.pages.map((page) => (
<a
key={page.name}
href={page.href}
className="flex items-center text-sm font-medium text-white"
>
{page.name}
</a>
))}
</div>
</Popover.Group>
</div>
{/* Mobile menu and search (lg-) */}
<div className="flex flex-1 items-center lg:hidden">
<button type="button" className="-ml-2 p-2 text-white" onClick={() => setMobileMenuOpen(true)}>
<span className="sr-only">Open menu</span>
<Bars3Icon className="h-6 w-6" aria-hidden="true" />
</button>
{/* Search */}
<a href="#" className="ml-2 p-2 text-white">
<span className="sr-only">Search</span>
<MagnifyingGlassIcon className="h-6 w-6" aria-hidden="true" />
</a>
</div>
{/* Logo (lg-) */}
<a href="#" className="lg:hidden">
<span className="sr-only">Your Company</span>
<img src="https://storage.googleapis.com/lullipop-image-dump/Motstudio_logo.svg" alt="" className="h-8 w-auto fill-white" />
</a>
<div className="flex flex-1 items-center justify-end">
<a href="#" className="hidden text-sm font-medium text-white lg:block">
Search
</a>
<div className="flex items-center lg:ml-8">
{/* Help */}
<a href="#" className="p-2 text-white lg:hidden">
<span className="sr-only">Help</span>
<QuestionMarkCircleIcon className="h-6 w-6" aria-hidden="true" />
</a>
<a href="#" className="hidden text-sm font-medium text-white lg:block">
Help
</a>
{/* Cart */}
<div className="ml-4 flow-root lg:ml-8">
<a href="#" className="group -m-2 flex items-center p-2">
<ShoppingBagIcon className="h-6 w-6 flex-shrink-0 text-white" aria-hidden="true" />
<span className="ml-2 text-sm font-medium text-white">0</span>
<span className="sr-only">items in cart, view bag</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</nav>
</header>
<div className="relative mx-auto flex max-w-3xl flex-col items-center px-6 py-32 text-center sm:py-64 lg:px-0">
<h1 className="text-4xl font-bold tracking-tight text-white lg:text-6xl">New arrivals are here</h1>
<p className="mt-4 text-xl text-white">
The new arrivals have, well, newly arrived. Check out the latest options from our summer small-batch release
while they're still in stock.
</p>
<a
href="#"
className="mt-8 inline-block rounded-md border border-transparent bg-white px-8 py-3 text-base font-medium text-gray-900 hover:bg-gray-100"
>
Shop New Arrivals
</a>
</div>
</div>
<main>
{/* Category section */}
<section aria-labelledby="category-heading" className="pt-24 sm:pt-32 xl:mx-auto xl:max-w-7xl xl:px-8">
<div className="px-4 sm:flex sm:items-center sm:justify-between sm:px-6 lg:px-8 xl:px-0">
<h2 id="category-heading" className="text-2xl font-bold tracking-tight text-gray-900">
Shop by Category
</h2>
<a href="#" className="hidden text-sm font-semibold text-indigo-600 hover:text-indigo-500 sm:block">
Browse all categories
<span aria-hidden="true"> →</span>
</a>
</div>
<div className="mt-4 flow-root">
<div className="-my-2">
<div className="relative box-content h-80 overflow-x-auto py-2 xl:overflow-visible">
<div className="absolute flex space-x-8 px-4 sm:px-6 lg:px-8 xl:relative xl:grid xl:grid-cols-5 xl:gap-x-8 xl:space-x-0 xl:px-0">
{categories.map((category) => (
<a
key={category.name}
href={category.href}
className="relative flex h-80 w-56 flex-col overflow-hidden rounded-lg p-6 hover:opacity-75 xl:w-auto"
>
<span aria-hidden="true" className="absolute inset-0">
<img src={category.imageSrc} alt="" className="h-full w-full object-cover object-center" />
</span>
<span
aria-hidden="true"
className="absolute inset-x-0 bottom-0 h-2/3 bg-gradient-to-t from-gray-800 opacity-50"
/>
<span className="relative mt-auto text-center text-xl font-bold text-white">{category.name}</span>
</a>
))}
</div>
</div>
</div>
</div>
<div className="mt-6 px-4 sm:hidden">
<a href="#" className="block text-sm font-semibold text-indigo-600 hover:text-indigo-500">
Browse all categories
<span aria-hidden="true"> →</span>
</a>
</div>
</section>
{/* Featured section */}
<section
aria-labelledby="social-impact-heading"
className="mx-auto max-w-7xl px-4 pt-24 sm:px-6 sm:pt-32 lg:px-8"
>
<div className="relative overflow-hidden rounded-lg">
<div className="absolute inset-0">
<img
src="https://tailwindui.com/img/ecommerce-images/home-page-01-feature-section-01.jpg"
alt=""
className="h-full w-full object-cover object-center"
/>
</div>
<div className="relative bg-gray-900 bg-opacity-75 px-6 py-32 sm:px-12 sm:py-40 lg:px-16">
<div className="relative mx-auto flex max-w-3xl flex-col items-center text-center">
<h2 id="social-impact-heading" className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
<span className="block sm:inline">Level up</span>
<span className="block sm:inline">your desk</span>
</h2>
<p className="mt-3 text-xl text-white">
Make your desk beautiful and organized. Post a picture to social media and watch it get more likes
than life-changing announcements. Reflect on the shallow nature of existence. At least you have a
really nice desk setup.
</p>
<a
href="#"
className="mt-8 block w-full rounded-md border border-transparent bg-white px-8 py-3 text-base font-medium text-gray-900 hover:bg-gray-100 sm:w-auto"
>
Shop Workspace
</a>
</div>
</div>
</div>
</section>
{/* Collection section */}
<section
aria-labelledby="collection-heading"
className="mx-auto max-w-xl px-4 pt-24 sm:px-6 sm:pt-32 lg:max-w-7xl lg:px-8"
>
<h2 id="collection-heading" className="text-2xl font-bold tracking-tight text-gray-900">
Shop by Collection
</h2>
<p className="mt-4 text-base text-gray-500">
Each season, we collaborate with world-class designers to create a collection inspired by the natural world.
</p>
<div className="mt-10 space-y-12 lg:grid lg:grid-cols-3 lg:gap-x-8 lg:space-y-0">
{collections.map((collection) => (
<a key={collection.name} href={collection.href} className="group block">
<div
aria-hidden="true"
className="aspect-h-2 aspect-w-3 overflow-hidden rounded-lg lg:aspect-h-6 lg:aspect-w-5 group-hover:opacity-75"
>
<img
src={collection.imageSrc}
alt={collection.imageAlt}
className="h-full w-full object-cover object-center"
/>
</div>
<h3 className="mt-4 text-base font-semibold text-gray-900">{collection.name}</h3>
<p className="mt-2 text-sm text-gray-500">{collection.description}</p>
</a>
))}
</div>
</section>
{/* Featured section */}
<section aria-labelledby="comfort-heading" className="mx-auto max-w-7xl px-4 py-24 sm:px-6 sm:py-32 lg:px-8">
<div className="relative overflow-hidden rounded-lg">
<div className="absolute inset-0">
<img
src="https://tailwindui.com/img/ecommerce-images/home-page-01-feature-section-02.jpg"
alt=""
className="h-full w-full object-cover object-center"
/>
</div>
<div className="relative bg-gray-900 bg-opacity-75 px-6 py-32 sm:px-12 sm:py-40 lg:px-16">
<div className="relative mx-auto flex max-w-3xl flex-col items-center text-center">
<h2 id="comfort-heading" className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
Simple productivity
</h2>
<p className="mt-3 text-xl text-white">
Endless tasks, limited hours, a single piece of paper. Not really a haiku, but we're doing our best
here. No kanban boards, burndown charts, or tangled flowcharts with our Focus system. Just the
undeniable urge to fill empty circles.
</p>
<a
href="#"
className="mt-8 block w-full rounded-md border border-transparent bg-white px-8 py-3 text-base font-medium text-gray-900 hover:bg-gray-100 sm:w-auto"
>
Shop Focus
</a>
</div>
</div>
</div>
</section>
</main>
<footer aria-labelledby="footer-heading" className="bg-gray-900">
<h2 id="footer-heading" className="sr-only">
Footer
</h2>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="py-20 xl:grid xl:grid-cols-3 xl:gap-8">
<div className="grid grid-cols-2 gap-8 xl:col-span-2">
<div className="space-y-12 md:grid md:grid-cols-2 md:gap-8 md:space-y-0">
<div>
<h3 className="text-sm font-medium text-white">Shop</h3>
<ul role="list" className="mt-6 space-y-6">
{footerNavigation.shop.map((item) => (
<li key={item.name} className="text-sm">
<a href={item.href} className="text-gray-300 hover:text-white">
{item.name}
</a>
</li>
))}
</ul>
</div>
<div>
<h3 className="text-sm font-medium text-white">Company</h3>
<ul role="list" className="mt-6 space-y-6">
{footerNavigation.company.map((item) => (
<li key={item.name} className="text-sm">
<a href={item.href} className="text-gray-300 hover:text-white">
{item.name}
</a>
</li>
))}
</ul>
</div>
</div>
<div className="space-y-12 md:grid md:grid-cols-2 md:gap-8 md:space-y-0">
<div>
<h3 className="text-sm font-medium text-white">Account</h3>
<ul role="list" className="mt-6 space-y-6">
{footerNavigation.account.map((item) => (
<li key={item.name} className="text-sm">
<a href={item.href} className="text-gray-300 hover:text-white">
{item.name}
</a>
</li>
))}
</ul>
</div>
<div>
<h3 className="text-sm font-medium text-white">Connect</h3>
<ul role="list" className="mt-6 space-y-6">
{footerNavigation.connect.map((item) => (
<li key={item.name} className="text-sm">
<a href={item.href} className="text-gray-300 hover:text-white">
{item.name}
</a>
</li>
))}
</ul>
</div>
</div>
</div>
<div className="mt-12 md:mt-16 xl:mt-0">
<h3 className="text-sm font-medium text-white">Sign up for our newsletter</h3>
<p className="mt-6 text-sm text-gray-300">The latest deals and savings, sent to your inbox weekly.</p>
<form className="mt-2 flex sm:max-w-md">
<label htmlFor="email-address" className="sr-only">
Email address
</label>
<input
id="email-address"
type="text"
autoComplete="email"
required
className="w-full min-w-0 appearance-none rounded-md border border-white bg-white px-4 py-2 text-base text-gray-900 placeholder-gray-500 shadow-sm focus:border-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-900"
/>
<div className="ml-4 flex-shrink-0">
<button
type="submit"
className="flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-gray-900"
>
Sign up
</button>
</div>
</form>
</div>
</div>
<div className="border-t border-gray-800 py-10">
<p className="text-sm text-gray-400">Copyright © 2021 Your Company, Inc.</p>
</div>
</div>
</footer>
</div>
)
}
Now that we have set up the product page, you should be greeted with a page resembling the one below:
This page is not just about aesthetics; it's the cornerstone of your e-commerce site. From here, users decide to make a purchase, so it's imperative that the design, content, and user experience are top-notch.
Enhancing the Product Page Experience
While having a product page is a good start, the real differentiation lies in the details. Let's explore a few ways to further enhance this page:
1. Detailed Product Descriptions
Having a clear, concise, yet comprehensive product description can answer any queries a potential customer might have, reducing the chances of cart abandonment.
Implementation Tip: Use bullet points for specifications or features to make the information easily digestible.
2. High-Quality Images and Videos
Having high-resolution images from multiple angles, as well as product videos, can provide users with a better understanding of the product.
Implementation Tip: Consider adding a zoom-in feature or an image gallery for a more immersive experience.
3. Customer Reviews and Ratings
Incorporate a section for customer reviews and ratings. Not only does it build trust, but it also provides valuable feedback.
Implementation Tip: Allow customers to add photos or videos with their reviews to give prospective buyers a real-world perspective.
4. Related Products
Display related products or "You might also like" suggestions at the bottom of the product page. This encourages users to continue browsing and potentially add more items to their cart.
Implementation Tip: Use machine learning algorithms or Shopify's recommendation APIs for better product suggestions.
With the above enhancements, your product page will not only be visually appealing but also user-friendly and optimized for conversions. Remember, the ultimate goal is to provide value to your users, making their shopping journey as seamless and enjoyable as possible.
In the next section, we'll delve deeper into integrating advanced checkout features, adding dynamic routes, and syncing to our Shopify Database to further elevate the user experience. Stay tuned!