Build and deploy a Next.js ecommerce website in 5 steps

Build and deploy a Next.js ecommerce website in 5 steps

ยท

5 min read

In this article, I'm going to show you how to build and deploy an ecommerce website in 5 steps. By leveraging the Cosmic React component library Blocks, we can build a high performance ecommerce website ready to accept product purchases in a matter of minutes. Let's go.

ecommerce-production.png

1. Install the Cosmic ecommerce Block

Start by installing a new Next.js app. Run the following command in your terminal application.

bunx create-next-app@latest cosmic-ecommerce && cd cosmic-ecommerce

Make sure to set the options to the following:

project-options.png

Next, log in to Cosmic and create a new empty Project.

create-ecommerce-project.png

Go to Project > Extensions and install the Cosmic Blocks extension. Find the ecommerce Block.

find-ecommerce-block.png

Follow the steps to install the Block content which includes content model and demo content.

install-ecommerce-block.png

Then run the command to install the ecommerce components in your codebase.

bunx @cosmicjs/blocks add ecommerce image-gallery

Create a new .env.local file and add your Cosmic API keys found in the Cosmic dashboard at Project > API keys:

# .env.local
COSMIC_BUCKET_SLUG=your_cosmic_bucket_slug
COSMIC_READ_KEY=your_cosmic_read_key
COSMIC_WRITE_KEY=your_cosmic_write_key

To create a shop page, add a new file located at app/shop/page.tsx with the following:

// app/shop/page.tsx
import { ProductList } from "@/cosmic/blocks/ecommerce/ProductList";
export default async function Shop() {
  return (
    <ProductList
      query={{ type: "products" }}
    />
  );
}

To create single product pages, add a new file located at app/shop/[slug]/page.tsx with the following:

// app/shop/[slug]/page.tsx
import { SingleProduct } from "@/cosmic/blocks/ecommerce/SingleProduct"
export default async function SingleProductPage({
  params,
  searchParams
}: {
    params: { slug: string }
    searchParams: {
      success?: string
    }
  }) {
  return (
    <SingleProduct
      query={{ slug: params.slug, type: "products" }}
      purchased={searchParams.success ? true : false}
    />
  );
}

Run the app to see the progress so far:

bun dev

You should see the store available at http://localhost:3000/shop (but not yet able to checkout with Stripe).

2. Configure Stripe

Next, we will enable checkout and payment processing through Stripe. First, install the Stripe clients with the following command:

bun add stripe @stripe/stripe-js

Add your Stripe API keys to the .env.local file. Find your Stripe API keys in the Stripe dashboard.

// .env.local
...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=change_to_your_stripe_public_key
STRIPE_SECRET_KEY=change_to_your_stripe_secret_key

Next, update your app/layout.tsx file to include the CartProvider to the layout. (Note: this assumes you've already added the Layout Block).

// app/layout.tsx
import "./globals.css";
import { Header } from "@/components/Header";
import { Footer } from "@/components/Footer";
import { CartProvider } from "@/cosmic/blocks/ecommerce/CartProvider";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <CartProvider>
          <Header />
          {children}
          <Footer />
        </CartProvider>
      </body>
    </html>
  );
}

Next, let's add the checkout component to components/Header.tsx.

// components/Header.tsx
import Link from "next/link";
import { cosmic } from "@/cosmic/client";
import { NavMenu } from "@/cosmic/blocks/navigation-menu/NavMenu";
import { CheckOut } from "@/cosmic/blocks/ecommerce/CheckOut";

export async function Header() {
  // Header data
  const { object: settings } = await cosmic.objects
    .findOne({
      type: "global-settings",
      slug: "settings",
    })
    .props("metadata")
    .depth(1);

  return (
    <div className="space-x-4 sticky top-0 bg-white/20 dark:bg-black/20 backdrop-blur-lg py-2 w-full z-[9999]">
      <div className="m-auto flex items-center md:container justify-between pl-2 pr-4">
        <Link href="/">
          <img
            src={`${settings.metadata.logo.imgix_url}?w=500&auto=format,compression`}
            alt={settings.metadata.company}
            className="h-10 m-auto dark:hidden"
          />
          <img
            src={`${settings.metadata.dark_logo.imgix_url}?w=500&auto=format,compression`}
            alt={settings.metadata.company}
            className="h-10 m-auto hidden dark:block"
          />
        </Link>
        <NavMenu query={{ type: "navigation-menus", slug: "header" }} />
        <CheckOut className="ml-4" productPath={"/shop"} />
      </div>
    </div>
  );
}

Next, let's add the checkout API route to our Next.js app located at app/api/checkout/route.ts with the following:

// app/api/checkout/route.ts
import { type NextRequest, NextResponse } from "next/server";
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);

export async function POST(request: NextRequest) {
  const res = await request.json();
  const stripe_product_ids = res.stripe_product_ids;
  try {
    let line_items = [];
    let mode = "payment";
    for (const stripe_product_id of stripe_product_ids) {
      const product = await stripe.products.retrieve(stripe_product_id);
      const price = await stripe.prices.retrieve(product.default_price);
      line_items.push({
        price: price.id,
        quantity: 1,
      });
      // If any items are recurring
      if (price.type === "recurring") mode = "subscription";
    }
    const session = await stripe.checkout.sessions.create({
      line_items,
      mode,
      success_url: `${res.redirect_url}/?success=true`,
      cancel_url: `${res.redirect_url}/?canceled=true`,
    });
    return Response.json({ url: session.url });
  } catch (err) {
    return NextResponse.json(err, { status: 500 });
  }
}

3. Install Stripe extension

Next, go to Project > Extensions in the Cosmic dashboard and install the Stripe Products Extension. This will enable the connection between the products in Cosmic and Stripe. After installing the extension to your Project, follow the steps to add your Stripe secret key to the extension configuration.

stripe-extension.png

4. Add products to Stripe

After installing the Stripe Products Extension, you should now see a button on the Product page to add the product to Stripe.

stripe-extension-product.png

Click this button to sync your Product data to Stripe including title, image, price, and option for recurring payments (subscription).

Next, go back to your app running locally and see that the checkout is now working on any product that has been connected with Stripe (you may need to refresh the page).

5. Deploy to your website hosting provider

Next, we'll deploy our ecommerce website to Vercel (which is a great choice to host your Next.js website). Other hosting options include Netlify and Render.

First, create a new git repository in GitHub and follow the steps to push your Next.js ecommerce website code.

new-repo.png

After you have pushed your code to the repository, go to the Vercel dashboard and add a new project connected to the code repository you just created in GitHub. (Note: This requires you to enable Vercel to access your GitHub repository.)

Then add your environment variables located in your .env.local file. Tip: You can add the full contents of the .env.local file by copy / pasting it into the first field in the Vercel form.

vercel-env-vars.png

Now deploy your code. ๐Ÿš€

You should now see a production build of your ecommerce website ready to take payments for your products and services. ๐Ÿ’ฐ

ecommerce-production.png

Next steps

I hope you enjoyed this quick tutorial on building an ecommerce website with Cosmic Blocks, Stripe, and Vercel. Check out more Blocks to explore to help you build optimized web experiences faster.

If you have any questions, reach out to me on X (fka Twitter) or on the Cosmic Discord server.

ย