Skip to content
Framework: Vite + React + TypeScriptDifficulty: intermediateFeatured

Ecommerce storefront

A product catalog with category filters and search, product detail view, and a cart kept in local state.

Catalog, product detail and cart screens driven by a typed in-memory product list. The cart is persisted to localStorage through a small hook, quantities can be adjusted inline, and the checkout form is explicitly a placeholder: it records the order locally and tells you where to plug in Stripe or Razorpay on the server.

  • react
  • ecommerce
  • cart
  • catalog
  • tailwind

This preview is the template's actual file tree and source code — exactly what is copied into a new project. Codexa does not publish screenshots or mock-ups of templates; run the project to see it rendered.

Files13 files

src/Cart.tsx83 lines
import { useState, type FormEvent } from "react";
import { formatPrice } from "./products";
import type { useCart } from "./cart";

export function Cart({ cart, onBack }: { cart: ReturnType<typeof useCart>; onBack: () => void }) {
  const [placed, setPlaced] = useState(false);

  function checkout(e: FormEvent) {
    e.preventDefault();
    // PLACEHOLDER: send the cart to your server, create a Stripe/Razorpay order there, then redirect to the hosted checkout.
    setPlaced(true);
    cart.clear();
  }

  if (placed) {
    return (
      <div className="mx-auto max-w-md text-center">
        <h1 className="text-2xl font-semibold">Order recorded locally</h1>
        <p className="mt-2 text-slate-600">No payment was taken. Wire the checkout form to a payment provider on your server.</p>
        <button type="button" onClick={onBack} className="mt-6 rounded-md bg-slate-900 px-4 py-2 text-sm text-white">
          Continue shopping
        </button>
      </div>
    );
  }

  return (
    <div className="grid gap-8 lg:grid-cols-[1fr_360px]">
      <section>
        <h1 className="text-2xl font-semibold tracking-tight">Your cart</h1>
        {cart.items.length === 0 ? (
          <p className="mt-6 text-slate-500">
            Your cart is empty.{" "}
            <button type="button" onClick={onBack} className="text-blue-600">
              Browse products
            </button>
          </p>
        ) : (
          <ul className="mt-6 divide-y divide-slate-200">
            {cart.items.map((l) => (
              <li key={l.productId} className="flex items-center justify-between gap-4 py-4">
                <div>
                  <p className="font-medium">{l.product.name}</p>
                  <p className="text-sm text-slate-500">{formatPrice(l.product.price)} each</p>
                </div>
                <div className="flex items-center gap-2">
                  <button type="button" onClick={() => cart.setQty(l.productId, l.qty - 1)} className="size-8 rounded-md border border-slate-300" aria-label={"Decrease quantity of " + l.product.name}>
                    −
                  </button>
                  <span className="w-6 text-center text-sm" aria-live="polite">
                    {l.qty}
                  </span>
                  <button type="button" onClick={() => cart.setQty(l.productId, l.qty + 1)} className="size-8 rounded-md border border-slate-300" aria-label={"Increase quantity of " + l.product.name}>
                    +
                  </button>
                </div>
              </li>
            ))}
          </ul>
        )}
      </section>
      <form onSubmit={checkout} className="h-fit space-y-3 rounded-xl border border-slate-200 p-5">
        <h2 className="font-medium">Checkout (placeholder)</h2>
        <label className="block text-sm">
          <span>Email</span>
          <input type="email" required className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" />
        </label>
        <label className="block text-sm">
          <span>Shipping address</span>
          <textarea required className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" rows={3} />
        </label>
        <div className="flex justify-between border-t border-slate-200 pt-3 text-sm">
          <span>Total</span>
          <span className="font-semibold">{formatPrice(cart.total)}</span>
        </div>
        <button type="submit" disabled={cart.items.length === 0} className="w-full rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white disabled:opacity-50">
          Place order
        </button>
        <p className="text-xs text-slate-500">No payment provider is connected in this template.</p>
      </form>
    </div>
  );
}

Dependencies

Documented from the template's package.json. The runtime has a prebuilt dependency set for Vite + React + TypeScript, so installs are fast.

PackageVersion
react^19.1.0
react-dom^19.1.0
vite^6.3.0
@vitejs/plugin-react^4.4.0
tailwindcss^4.1.0
@tailwindcss/vite^4.1.0
typescript^5.8.0

How it runs

Framework
Vite + React + TypeScript
Dev command
npm run dev
Build command
npm run build
Output directory
dist
Port
5173
Opens at
/
Difficulty
intermediate

Projects run in an isolated sandbox with a live preview. Read the projects documentation for details on settings, snapshots and deployment.

Need the raw data? GET /api/v1/templates/vite-react-storefront returns this template with its files.

Build on Ecommerce storefront

Create a free account, start from this template, and ask the AI agent to turn it into your app.