How to Build a Production-Ready eCommerce Website with React, Tailwind CSS, PlanetScale and Stripe
In this expert guide, we‘ll walk through building a complete, production-ready ecommerce website from the ground up using some of the most powerful and developer-friendly tools available today:
- React for creating dynamic, component-based UIs
- Tailwind CSS for styling with a utility-first approach
- PlanetScale for a scalable serverless MySQL database
- Stripe for secure payment processing
By the end, you‘ll have a fully-functional ecommerce site ready to launch and grow your online business. Let‘s dive in!
Project Setup
First, make sure you have Node.js installed. Open your terminal and navigate to the directory where you want to create your project. Run the following commands:
npx create-next-app@latest ecommerce-site
cd ecommerce-site
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
This creates a new Next.js app (which uses React), installs Tailwind CSS, and creates the necessary config files.
Replace the contents of tailwind.config.js
with:
module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
And add the Tailwind directives to styles/globals.css
:
@tailwind base;
@tailwind components;
@tailwind utilities;
Run npm run dev
and open http://localhost:3000
to see the default Next.js starter.
Creating Components and Pages
Let‘s start building the site! We‘ll use React components to create reusable UI elements and Next.js pages for each main view.
The ecommerce site will have the following main pages:
- Home page with featured products
- Product catalog
- Individual product details
- Shopping cart
- Checkout
- Order confirmation
Plus some supplementary pages like About, Contact, FAQ, etc.
Each page will be made up of components like the Header, Footer, ProductCard, CartItem, etc. Components allow us to break the UI into independent, reusable pieces.
Here‘s an example of a simple Header
component using Tailwind CSS classes:
const Header = () => (
<header className="sticky top-0 bg-white shadow-md z-50">
<div className="max-w-6xl mx-auto px-4">
<div className="flex justify-between items-center py-4">
<div className="flex items-center">
<img src="/logo.svg" alt="Logo" className="h-8 w-auto"/>
<span className="ml-2 text-xl font-semibold">My Store</span>
</div>
<nav>
<ul className="flex space-x-4">
<li>
<Link href="/">
<a className="text-gray-600 hover:text-blue-600">Home</a>
</Link>
</li>
<li>
<Link href="/products">
<a className="text-gray-600 hover:text-blue-600">Products</a>
</Link>
</li>
<li>
<Link href="/cart">
<a className="text-gray-600 hover:text-blue-600">Cart</a>
</Link>
</li>
</ul>
</nav>
</div>
</div>
</header>
)
This uses Tailwind‘s pre-defined utility classes to style the header, handle layout, and add hover effects. No custom CSS needed!
Other key components will include:
ProductCard
to display a product image, name, price, and "Add to Cart" buttonCartItem
to display a product in the cart with quantity controlsCheckoutForm
to capture shipping and payment details
By composing an entire UI from smaller components, the codebase stays organized and maintainable as it grows.
Integrating with PlanetScale
For the backend database to power our ecommerce site, we‘ll use PlanetScale – a serverless MySQL platform that automatically scales, supports branching workflows, and integrates easily with Prisma ORM.
First, sign up for a free PlanetScale account and create a new database. Install the PlanetScale CLI and Prisma:
npm install -g planetscale prisma
Run pscale auth login
to connect the CLI to your PlanetScale account. Then create a new branch to develop our database schema:
pscale branch create ecommerce-site init
In your project directory, run prisma init
to set up Prisma. Open the generated schema.prisma
file and add:
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
relationMode = "prisma"
}
model Product {
id Int @id @default(autoincrement())
name String
description String
price Int
image String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Order {
id Int @id @default(autoincrement())
items Json
total Int
status String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
This defines the Product
and Order
models that will be stored in the database, with fields like name, price, creation date, order status, etc.
To get the DATABASE_URL
for the Prisma datasource:
pscale connect ecommerce-site init --format prisma
Copy the output and add it to a new .env
file. Then run:
prisma generate
prisma db push
To generate the Prisma client and push the schema to the PlanetScale database.
Now we can use Prisma in our API routes to query and update data. For example, here‘s an API route to get a list of products:
import prisma from ‘lib/prisma‘
export default async function handler(req, res) {
if (req.method === ‘GET‘) {
const products = await prisma.product.findMany()
res.json(products)
}
}
And one to create a new order:
import prisma from ‘lib/prisma‘
export default async function handler(req, res) {
if (req.method === ‘POST‘) {
const { items, total } = req.body
const order = await prisma.order.create({
data: {
items,
total,
status: ‘pending‘
}
})
res.json(order)
}
}
On the frontend, we can use fetch
or a library like SWR to query the API routes and display the returned data.
PlanetScale and Prisma make it incredibly easy to develop and manage a production-grade database. Branching allows us to prototype schema changes safely, while PlanetScale‘s autoscaling means the database will perform reliably under load.
Processing Payments with Stripe
To securely accept payments, we‘ll integrate Stripe Checkout into the frontend and backend of our ecommerce site.
First, sign up for a Stripe account and get your API keys. Install the Stripe Node library:
npm install stripe
And add your Stripe secret key to .env
:
STRIPE_SECRET_KEY=sk_test_...
To initiate the checkout process when a customer clicks the "Checkout" button, we‘ll make a request to a new API route that creates a Stripe Checkout session:
import Stripe from ‘stripe‘;
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
export default async function handler(req, res) {
if (req.method === ‘POST‘) {
try {
const { items } = req.body;
const session = await stripe.checkout.sessions.create({
payment_method_types: [‘card‘],
line_items: items.map(item => ({
price_data: {
currency: ‘usd‘,
product_data: {
name: item.name,
images: [item.image],
},
unit_amount: item.price * 100,
},
quantity: item.qty,
})),
mode: ‘payment‘,
success_url: `${req.headers.origin}/success`,
cancel_url: `${req.headers.origin}/cart`,
});
res.json({ id: session.id });
} catch (err) {
res.status(500).json({ error: err.message })
}
}
}
This creates a new Checkout session with the provided line items and returns the session ID to the frontend.
On the frontend, we initialize Stripe and redirect to the returned Checkout session URL:
import { loadStripe } from ‘@stripe/stripe-js‘;
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY);
const CheckoutButton = ({ items }) => {
const handleCheckout = async () => {
const stripe = await stripePromise;
const res = await fetch(‘/api/checkout‘, {
method: ‘POST‘,
headers: { ‘Content-Type‘: ‘application/json‘ },
body: JSON.stringify({ items }),
});
const { id } = await res.json();
await stripe.redirectToCheckout({ sessionId: id });
}
return (
<button
className="bg-blue-500 text-white px-4 py-2 rounded"
onClick={handleCheckout}
>
Checkout
</button>
)
}
When the customer completes the checkout, Stripe will redirect back to our success_url
. Here we can show an order confirmation and save the order details to our database.
With Stripe Checkout, you can easily accept payments while letting Stripe handle all the complexities of PCI compliance, fraud protection, and more.
Optimizing for Production
To ensure our ecommerce site performs well and provides an excellent user experience, there are a few key things to optimize:
- Page load speed – Use
next/image
for optimized images, minimize and cache assets, lazy load below-the-fold content. Aim for a Lighthouse performance score of 90+. - Responsive design – Use Tailwind‘s responsive utility classes to ensure the site looks great and functions well on any device size. Test thoroughly on real devices.
- Accessibility – Follow WAI-ARIA guidelines, use semantic HTML, provide text alternatives for media, ensure the site is keyboard navigable. Aim for a Lighthouse accessibility score of 100.
- SEO – Add optimized title tags and meta descriptions to each page, use structured data where relevant, generate an XML sitemap, consider using
next/head
to set per-page metadata. - Security – Keep dependencies up to date, use HTTPS, secure authentication, sanitize user input, limit attack surface. Consider security scanning tools and pentesting before launch.
Before launching, thoroughly test the entire checkout flow, test with different user roles (customer, admin, etc.) and do some load testing to ensure the site remains performant under heavy traffic.
Deployment
Finally, it‘s time to deploy our production-ready ecommerce site and share it with the world!
Deploying a Next.js app is easy with Vercel. Sign up for a free account, connect your Git repository, and Vercel will automatically build and deploy the app with each new commit.
For the backend database, promote your PlanetScale init
branch to the main
production branch:
pscale deploy-request create ecommerce-site init
Review and approve the deploy request in the PlanetScale web UI. Your database schema will be deployed to production and ready to serve live traffic.
Wrap-up
And there you have it! A complete, production-ready ecommerce website powered by some of the best tools in web development.
Of course, this is just the beginning. You can further extend your site with features like:
- User accounts and order history
- Product search and filtering
- Related product recommendations
- Discount codes and promotions
- Wishlists and saved carts
- Product reviews and ratings
- Inventory management
- Abandoned cart recovery
- Multi-language and multi-currency
By using React, Tailwind CSS, PlanetScale, Prisma, Next.js, and Stripe, you have a solid foundation to grow your ecommerce business while delivering a first-class customer experience. The possibilities are endless!