DEV Community

Cover image for 🌱 Responsive Planto Ecommerce Website with React, Vite, TypeScript & Tailwind CSS 🌱
Codex
Codex

Posted on

🌱 Responsive Planto Ecommerce Website with React, Vite, TypeScript & Tailwind CSS 🌱

🌱 Responsive Planto Ecommerce Website with React, Vite, TypeScript & Tailwind CSS 🌱

Welcome to an exciting journey in modern web development! πŸš€ In this project, we’ll create a fully responsive ecommerce website for selling herbs using cutting-edge technologies: React, Vite, TypeScript, and Tailwind CSS. This project is perfect for developers of all levels looking to sharpen their skills while building a practical application.


πŸ’» Features:

  • Stunning Home Page: An eye-catching design to welcome your visitors.
  • Smooth Checkout Process: Simplify purchasing with a user-friendly checkout page.
  • Detailed Plant Product Pages: Highlight the unique benefits of each plant.
  • Organized Plant Types Page: Help users browse by category.
  • Functional Contact Page: Enable users to get in touch effortlessly.

πŸ“¦ What It contains:

  • Responsive Design Best Practices: Ensure your website looks great on any device.
  • Modern Component-Based Development: Build clean, reusable components.
  • Tailwind CSS for Fast and Elegant Styling: Speed up styling with utility-first CSS.

✨ Why Build This Project?

This ecommerce project is an excellent way to master:

  • Developing responsive layouts.
  • Using TypeScript for scalable and maintainable code.
  • Implementing Tailwind CSS to craft modern, visually appealing UIs.

Whether you’re a beginner or an experienced developer, this project will help you gain valuable insights into crafting dynamic web applications.


🚧 Tech Stack:

  • React for UI Development
  • Vite for Blazing-Fast Development
  • TypeScript for Type Safety
  • Tailwind CSS for Utility-First Styling

πŸ”¨ Code Overview:

Here’s a glimpse of how the app is structured:

App.tsx

import { useState, useEffect } from 'react';
import { Routes, Route, useLocation } from 'react-router-dom'; 
import { Cart, Footer, Navbar } from './components';
import { Home, Checkout, PlantsType, Contact, PlantDetail } from './pages';
import { useCart } from './context/CartContext';

export default function App() {
  const [isNavOpen, setIsNavOpen] = useState(false);
  const [isCartOpen, setIsCartOpen] = useState(false);
  const location = useLocation();

  const { cart, updateQuantity, removeFromCart } = useCart();
  const closeCart = () => setIsCartOpen(false);

  useEffect(() => {
    closeCart();
    window.scrollTo(0, 0);
  }, [location]);

  return (
    <div className="min-h-screen bg-sectionColor text-white font-sans relative">
      <Navbar
        isOpen={isNavOpen}
        setIsOpen={setIsNavOpen}
        cart={cart}
        toggleCart={() => setIsCartOpen(prev => !prev)}
      />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/checkout" element={<Checkout />} />
        <Route path="/plants-type" element={<PlantsType />} />
        <Route path="/plant/:plantId" element={<PlantDetail />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
      {isCartOpen && (
          <Cart
            cart={cart}
            updateQuantity={updateQuantity}
            removeFromCart={removeFromCart}
            closeCart={closeCart}
          />
      )}
      <Footer />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Home.tsx

import React from 'react';
import { HeroSection, TopSelling, CustomerReview, BestO2 } from '../components';
import plantsData from '../data/plantsData';

const Home: React.FC = () => {
  return (
    <div>
      <HeroSection />
      <main className="container mx-auto px-4 py-8">
        <TopSelling plants={plantsData} />
        <CustomerReview />
        <BestO2 plants={plantsData} />
      </main>
    </div>
  );
};

export default Home;
Enter fullscreen mode Exit fullscreen mode

Cart.tsx

import React from 'react';
import { useNavigate } from 'react-router-dom';
import { Button }from './';
import { CartProps } from '../types/types';
import { PlusIcon, MinusIcon, TrashIcon, XMarkIcon } from "@heroicons/react/24/solid";

const Cart: React.FC<CartProps> = ({ cart, updateQuantity, removeFromCart, closeCart }) => {
  const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
  const navigate = useNavigate(); 


  const handleCheckout = () => {
    const checkoutData = cart.map(({ id, name, price, quantity }) => ({
      id,
      name,
      price,
      quantity,
    }));

    closeCart();
    navigate('/checkout', { state: { cart: checkoutData } }); 
  };

  return (
    <div className="fixed top-16 right-0 w-full max-w-md bg-[#11170f] rounded-3xl p-4 z-50  max-h-[80vh] overflow-y-auto">
      <div className="container mx-auto px-4 py-8 relative">
        <button 
          className="absolute top-4 right-4 text-gray-300 hover:text-white"
          onClick={closeCart} 
          aria-label="Close cart"
        >
          <XMarkIcon className="h-6 w-6" />
        </button>
        <h1 className="text-3xl font-bold mb-8">Your Cart</h1>
        {cart.length === 0 ? (
          <p>Your cart is empty.</p>
        ) : (
          <div className="grid md:grid-cols-1 gap-8">
            <div className="md:col-span-1">
              {/* Scrollable container for cart items */}
              <div className="max-h-72 overflow-y-auto"> 
                {cart.map((item) => (
                  <div key={item.id} className="flex items-center mb-4 bg-[#d8ebd1] bg-opacity-30 p-4 rounded-lg">
                    <img src={item.image} alt={item.name} className="w-20 h-20 object-cover rounded-md mr-4" />
                    <div className="flex-1">
                      <h3 className="text-lg font-semibold">{item.name}</h3>
                      <p className="text-gray-300">Rs. {item.price}</p>
                      <div className="flex items-center mt-2">
                        <button 
                          className="text-gray-300 hover:text-white"
                          onClick={() => updateQuantity(item.id, Math.max(1, item.quantity - 1))}
                          aria-label="Decrease quantity"
                        >
                          <MinusIcon className="h-4 w-4" />
                        </button>
                        <span className="mx-2">{item.quantity}</span>
                        <button 
                          className="text-gray-300 hover:text-white"
                          onClick={() => updateQuantity(item.id, item.quantity + 1)}
                          aria-label="Increase quantity"
                        >
                          <PlusIcon className="h-4 w-4" />
                        </button>
                      </div>
                    </div>
                    <button 
                      className="text-gray-300 hover:text-white"
                      onClick={() => removeFromCart(item.id)}
                      aria-label={`Remove ${item.name} from cart`}
                    >
                      <TrashIcon className="h-6 w-6" />
                    </button>
                  </div>
                ))}
              </div>
            </div>
            <div className="bg-[#d8ebd1] bg-opacity-30 p-4 rounded-lg h-fit">
              <h3 className="text-xl font-semibold mb-4">Order Summary</h3>
              <div className="space-y-2 mb-4">
                <div className="flex justify-between">
                  <span>Subtotal</span>
                  <span>Rs. {total}</span>
                </div>
                <div className="flex justify-between">
                  <span>Shipping</span>
                  <span>Rs. 50</span>
                </div>
                <div className="flex justify-between font-semibold">
                  <span>Total</span>
                  <span>Rs. {total + 50}</span>
                </div>
              </div>
              <div className="flex justify-end">
                <Button text="Confirm Order" onClick={handleCheckout} />
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

export default Cart;
Enter fullscreen mode Exit fullscreen mode

πŸ“₯ Resources:


πŸ‘€ Connect & Share

If you find this project helpful, don’t forget to like, share, and subscribe! Let’s grow together. 🌟

Feel free to leave your feedback in the comments below. Happy coding! πŸš€


Tags:

#React #Vite #TypeScript #TailwindCSS #WebDevelopment #ResponsiveDesign #reactjs #js

Top comments (0)