A modern, responsive, and animated frontend website built with Next.js 14, React, TypeScript, and TailwindCSS. This project demonstrates how to build a professional skincare salon website with smooth animations, custom cursor interactions, and a modular component structure.
Live Demo: https://skincare-salon.vercel.app/
- Project Overview
- Features
- Tech Stack
- Prerequisites
- Installation
- Environment Variables
- Running the Project
- Project Structure
- Components Overview
- Routes & Pages
- How It Works
- Code Examples
- Customization Guide
- Deployment
- Keywords
- Conclusion
This is a full-stack frontend project for a premium skincare salon showcasing:
- Home Page: Hero section with animated content and video modal
- About Page: Company information with animated statistics
- Treatments Page: Service offerings in a grid layout
- Contact Page: Contact form and business information
The project uses Next.js 14 App Router, Framer Motion for animations, and a custom cursor context for enhanced user interactions. All components are built with TypeScript for type safety and maintainability.
- Modern UI/UX: Clean, professional design with smooth animations
- Responsive Design: Mobile-first approach with breakpoints for all screen sizes
- Custom Cursor: Interactive cursor that changes on hover (desktop only)
- Page Transitions: Smooth fade-in/fade-out effects between pages
- Animated Statistics: CountUp animations for engaging number displays
- Video Modal: Embedded video player using ReactPlayer
- SEO Optimized: Comprehensive metadata for search engines and social sharing
- TypeScript: Full type safety throughout the application
- Component-Based Architecture: Reusable, modular components
- Performance Optimized: Next.js Image optimization and font optimization
- Framework: Next.js 14.2.35 (React 18, App Router)
- Language: TypeScript 5
- Styling: TailwindCSS 3.4.1
- Animations: Framer Motion 11.3.29
-
UI Components:
@radix-ui/react-dialog- Accessible dialog/modal componentslucide-react- Icon libraryreact-icons- Additional icon sets
-
Utilities:
clsx- Conditional className utilitytailwind-merge- Merge Tailwind classes intelligentlyclass-variance-authority- Component variant management
-
Features:
react-player- Video player for YouTube/Vimeoreact-countup- Animated number countingreact-responsive- Media query hooks
- Linting: ESLint with Next.js config
- Type Checking: TypeScript strict mode
- Build Tool: Next.js built-in webpack/bundler
Before you begin, ensure you have the following installed:
- Node.js: Version 18.x or higher (Download Node.js)
- npm or yarn or pnpm or bun (package manager)
- Git (for cloning the repository)
- Code Editor: VS Code recommended (with ESLint and Prettier extensions)
git clone <repository-url>
cd skincare-salonnpm install
# or
yarn install
# or
pnpm install
# or
bun installThis will install all required dependencies listed in package.json.
Check that all packages are installed correctly:
npm list --depth=0This project uses optional environment variables for configuration. Create a .env.local file in the root directory (this file is gitignored for security).
Note: Currently, environment variables are optional. The project has fallback values in the code.
Create .env.local file:
# Site URL (used for metadata and absolute URLs)
# Default fallback: https://skincare-salon-arnob.vercel.app
NEXT_PUBLIC_SITE_URL=https://your-domain.com
# Add any other environment variables as needed
# NEXT_PUBLIC_API_URL=https://api.example.com-
Create the file: In the root directory, create a new file named
.env.local -
Add variables: Copy the template above and add your values
-
Restart dev server: After adding/changing environment variables, restart your development server:
npm run dev.env.localis gitignored and should never be committed- Variables prefixed with
NEXT_PUBLIC_are exposed to the browser - Variables without the prefix are server-side only
- Never commit sensitive information (API keys, secrets, etc.)
NEXT_PUBLIC_SITE_URL=https://skincare-salon-arnob.vercel.appStart the development server with hot-reloading:
npm run dev
# or
yarn dev
# or
pnpm devOpen http://localhost:3000 in your browser to see the application.
Build the project for production:
npm run buildThis creates an optimized production build in the .next directory.
After building, start the production server:
npm startCheck code quality and find potential issues:
npm run lintskincare-salon/
βββ app/ # Next.js App Router directory
β βββ about/ # About page route
β β βββ page.tsx # About page component
β βββ contact/ # Contact page route
β β βββ page.tsx # Contact page component
β βββ treatments/ # Treatments page route
β β βββ page.tsx # Treatments page component
β βββ favicon.ico # Site favicon
β βββ globals.css # Global styles and Tailwind imports
β βββ layout.tsx # Root layout component
β βββ page.tsx # Home page component
β
βββ components/ # React components
β βββ Form.tsx # Contact form component
β βββ Header.tsx # Main header/navigation
β βββ InfoItem.tsx # Reusable info display component
β βββ ModalVideo.tsx # Video modal component
β βββ Socials.tsx # Social media links component
β βββ StatsItem.tsx # Statistics counter component
β βββ TreatmentsItem.tsx # Treatment card component
β βββ Nav/ # Navigation components
β β βββ MobileNav.tsx # Mobile navigation menu
β β βββ Nav.tsx # Desktop navigation
β βββ Transition/ # Page transition components
β β βββ PageTransition.tsx # Page fade transition
β β βββ Transition.tsx # Page overlay transition
β βββ ui/ # shadcn/ui components
β βββ dialog.tsx # Dialog/modal component
β
βββ context/ # React Context providers
β βββ CursorContext.tsx # Custom cursor context
β
βββ lib/ # Utility functions
β βββ utils.ts # Helper functions (cn utility)
β
βββ public/ # Static assets
β βββ assets/ # Images and icons
β βββ about/
β βββ contact/
β βββ home/
β βββ treatments/
β βββ logo.svg
β
βββ utils/ # Utility modules
β βββ links.ts # Navigation links data
β
βββ .eslintrc.json # ESLint configuration
βββ .gitignore # Git ignore rules
βββ components.json # shadcn/ui configuration
βββ next.config.mjs # Next.js configuration
βββ package.json # Dependencies and scripts
βββ postcss.config.mjs # PostCSS configuration
βββ tailwind.config.ts # TailwindCSS configuration
βββ tsconfig.json # TypeScript configurationThe root layout component that wraps all pages. It includes:
- Font optimization (Marcellus & Montserrat from Google Fonts)
- SEO metadata configuration
- Global providers (CursorProvider)
- Shared UI elements (Header, Transitions)
Key Features:
- Font variables for CSS usage
- Comprehensive SEO metadata
- Persistent layout across route changes
Main navigation header component featuring:
- Top contact bar (phone, email, social links)
- Logo with home link
- Desktop navigation menu
- Mobile hamburger menu
- Slide-out mobile navigation panel
Props: None (uses internal state)
Usage Example:
import Header from "@/components/Header";
// In layout.tsx
<Header />;Landing page with hero section, featuring:
- Animated heading and description
- Call-to-action button
- Video modal trigger
- Hero image with slide-up animation
Key Animations:
- Fade-in on page load (2s delay)
- Text slides down from top
- Image slides up from bottom
About page displaying:
- Company image
- Mission statement
- Animated statistics (CountUp)
- Contact CTA button
Statistics Shown:
- 13 Years On Market
- 35k+ Happy Clients
- 97% Natural Ingredients
Services/treatments page with:
-
Treatment cards in grid layout
-
Four main services:
- Classic Facial
- Chemical Peel
- Eyebrow Waxing/Shaping
- HydraFacial
Contact page featuring:
- Contact information (address, phone, email)
- Contact form
- Two-column responsive layout
Contact form component with:
- Full name input
- Email input
- Phone input
- Message textarea
- Submit button
Note: Currently a presentational component. Form submission handling would need to be added (e.g., using Next.js Server Actions or API routes).
Usage Example:
import Form from "@/components/Form";
<Form />;Animated statistics counter component.
Props:
{
countNum: number; // Target number to count to
countText?: string; // Optional suffix (%, k+, etc.)
text: string; // Description text
}Usage Example:
import StatsItem from "@/components/StatsItem";
<StatsItem countNum={13} text="Years On Market" />
<StatsItem countNum={35} countText="k+" text="Happy Clients" />
<StatsItem countNum={97} countText="%" text="Natural Ingredients" />Features:
- CountUp animation (2.4s delay, 6s duration)
- Optional text suffix for units
Reusable component for displaying information with icons.
Props:
{
imgSrc: `/${string}`; // Icon image path (must start with /)
title: string; // Heading text
description: React.ReactNode; // Flexible content (text, HTML, components)
}Usage Example:
import InfoItem from "@/components/InfoItem";
<InfoItem
imgSrc="/assets/contact/pin.svg"
title="Address"
description={
<p>
123/45 Elm St, Suite 800
<br />
Los Angeles, CA 90012
</p>
}
/>;Treatment/service card component.
Props:
{
title: string; // Treatment name
description: string; // Treatment description
}Usage Example:
import TreatmentsItem from "@/components/TreatmentsItem";
<TreatmentsItem
title="Classic Facial"
description="Deep cleansing, exfoliation and hydration for a refreshed complexion"
/>;Features:
- Decorative accent shape
- Responsive alignment (centered on mobile, left on desktop)
Video modal component using shadcn/ui Dialog.
Features:
- Play button trigger
- YouTube video player
- Accessible modal dialog
Usage Example:
import ModalVideo from "@/components/ModalVideo";
<ModalVideo />;Customization: Update the YouTube URL in the component to change the video.
Social media links component.
Props:
{
containerStyle: string; // Tailwind CSS classes for styling
}Usage Example:
import Socials from "@/components/Socials";
<Socials containerStyle="flex gap-6 text-white" />;Customization: Update the socialLinks array in the component to change links and icons.
Desktop navigation component.
Features:
- Active link highlighting
- Uses
usePathname()hook to detect current route
Usage Example:
import Nav from "@/components/Nav/Nav";
<Nav />;Mobile slide-out navigation panel.
Props:
{
setMobileNav: React.Dispatch<React.SetStateAction<boolean>>;
}Features:
- Close button
- Vertical link list
- Social media links at bottom
- Active link highlighting
Page transition overlay component that creates a slide-up effect.
Features:
- AnimatePresence for exit animations
- Syncs with route changes using pathname key
Page fade-out wrapper component.
Props:
{
children: React.ReactNode;
}Usage Example:
import PageTransition from "@/components/Transition/PageTransition";
<PageTransition>{children}</PageTransition>;Custom cursor context provider.
Features:
- Spring-animated cursor following mouse
- Size change on hover
- Color change on hover
- Disabled on mobile/tablet (< 1200px)
Usage Example:
import { useCursor } from "@/context/CursorContext";
const MyComponent = () => {
const { mouseEnterHandler, mouseLeaveHandler } = useCursor();
return (
<div onMouseEnter={mouseEnterHandler} onMouseLeave={mouseLeaveHandler}>
Hover me!
</div>
);
};Methods:
mouseEnterHandler(): Expands cursor and changes colormouseLeaveHandler(): Resets cursor to default
Utility functions, primarily the cn function.
cn Function: Combines clsx and tailwind-merge for optimal className handling.
Usage Example:
import { cn } from "@/lib/utils";
// Conditionally apply classes
<div className={cn("base-class", isActive && "active-class", "another-class")} />
// Resolves Tailwind conflicts
<div className={cn("px-2 py-1", "px-4")} />
// Result: "py-1 px-4" (px-2 is overridden by px-4)Centralized navigation links data.
Usage Example:
import { links } from "@/utils/links";
links.map((link) => (
<Link key={link.href} href={link.href}>
{link.name}
</Link>
));Next.js 13+ App Router uses the file system for routing:
| Route | File | Description |
|---|---|---|
/ |
app/page.tsx |
Home page with hero section |
/about |
app/about/page.tsx |
About page with statistics |
/treatments |
app/treatments/page.tsx |
Services/treatments page |
/contact |
app/contact/page.tsx |
Contact page with form |
app/
βββ page.tsx β /
βββ about/
β βββ page.tsx β /about
βββ treatments/
β βββ page.tsx β /treatments
βββ contact/
βββ page.tsx β /contact
Navigation links are centralized in utils/links.ts:
const links = [
{ href: "/", name: "Home" },
{ href: "/about", name: "About" },
{ href: "/treatments", name: "Treatments" },
{ href: "/contact", name: "Contact" },
];Next.js 14 uses the App Router (file-system based routing):
- Layouts:
app/layout.tsxwraps all pages, providing shared UI - Pages: Each
page.tsxfile becomes a route - Server & Client Components: Server components by default, use
"use client"for interactivity
The project uses Framer Motion for animations:
- Page Transitions: Coordinated fade-out/fade-in effects
- Component Animations: Individual elements animate on mount
- Staggered Delays: Animations are timed to create smooth sequences
Animation Flow:
- Page loads β Transition overlay slides up (0-0.6s)
- Page content fades in (2s delay)
- Individual elements animate with staggered delays
The custom cursor is implemented using:
- Context Provider:
CursorContextwraps the app - Motion Values:
useMotionValuetracks mouse position - Spring Physics:
useSpringcreates smooth following effect - State Management: Size and color change based on hover state
TailwindCSS breakpoints:
sm: 640pxmd: 768pxlg: 960pxxl: 1200px
Mobile-First Approach: Styles target mobile by default, then adjust for larger screens:
<div className="flex-col xl:flex-row">
{/* Stacked on mobile, row on desktop */}
</div>- Create the route file:
// app/services/page.tsx
"use client";
import { motion } from "framer-motion";
export default function Services() {
return (
<motion.section
initial={{ opacity: 0 }}
animate={{ opacity: 1, transition: { delay: 2 } }}
className="min-h-screen"
>
<h1>Our Services</h1>
</motion.section>
);
}- Add to navigation (optional):
// utils/links.ts
const links = [
// ... existing links
{ href: "/services", name: "Services" },
];// components/Card.tsx
import Image from "next/image";
type CardProps = {
title: string;
description: string;
image: string;
};
export default function Card({ title, description, image }: CardProps) {
return (
<div className="bg-white rounded-lg shadow-md p-6">
<Image src={image} alt={title} width={300} height={200} />
<h3 className="text-xl font-bold mt-4">{title}</h3>
<p className="text-gray-600 mt-2">{description}</p>
</div>
);
}Usage:
import Card from "@/components/Card";
<Card
title="Service Name"
description="Service description"
image="/assets/service.jpg"
/>;"use client";
import { motion } from "framer-motion";
export default function AnimatedSection() {
return (
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: "easeOut" }}
>
Content here
</motion.div>
);
}"use client";
import { useCursor } from "@/context/CursorContext";
export default function InteractiveElement() {
const { mouseEnterHandler, mouseLeaveHandler } = useCursor();
return (
<button
onMouseEnter={mouseEnterHandler}
onMouseLeave={mouseLeaveHandler}
className="btn"
>
Hover Me
</button>
);
}// components/Form.tsx
"use client";
import { useState } from "react";
export default function Form() {
const [formData, setFormData] = useState({
name: "",
email: "",
message: "",
});
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Add your form submission logic here
// Example: API route or Server Action
const response = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(formData),
});
// Handle response
};
return <form onSubmit={handleSubmit}>{/* Form fields */}</form>;
}Edit tailwind.config.ts:
colors: {
primary: {
DEFAULT: "#473936", // Change primary color
},
secondary: {
DEFAULT: "#f2dfce",
100: "#cea39c",
},
accent: {
DEFAULT: "#f19687", // Change accent color
100: "#f2d5c5",
},
}- Update font imports in
app/layout.tsx:
import { YourFont } from "next/font/google";
const yourFont = YourFont({
subsets: ["latin"],
weight: ["400", "700"],
variable: "--font-yourfont",
});- Update Tailwind config:
fontFamily: {
primary: "var(--font-yourfont)",
}- Create new directory in
app/:
mkdir app/new-page- Create
page.tsx:
// app/new-page/page.tsx
export default function NewPage() {
return <div>New Page Content</div>;
}- Add to navigation (optional):
// utils/links.ts
{ href: "/new-page", name: "New Page" }Adjust animation timings in component files:
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{
delay: 2, // Change delay
duration: 1, // Change duration
ease: "easeInOut" // Change easing
}}
>- Push to GitHub:
git add .
git commit -m "Ready for deployment"
git push origin main-
Deploy on Vercel:
- Go to vercel.com
- Import your repository
- Vercel auto-detects Next.js settings
- Click "Deploy"
-
Environment Variables (if needed):
- Add in Vercel dashboard β Settings β Environment Variables
-
Build settings:
- Build command:
npm run build - Publish directory:
.next
- Build command:
-
Deploy:
- Connect repository on Netlify
- Configure build settings
- Deploy
- AWS Amplify: Supports Next.js out of the box
- DigitalOcean App Platform: Configure Next.js buildpack
- Docker: Create Dockerfile for containerized deployment
Next.js, React, TypeScript, TailwindCSS, Framer Motion, shadcn/ui, Radix UI, Dialog, Lucide React, React Icons, React Player, React CountUp, Responsive Design, Animation, Modern Web, Static Website, Portfolio, UI/UX, Custom Cursor, Page Transitions, SEO, App Router, Server Components, Client Components, Skincare Salon, Beauty Services, Facial Treatments, HydraFacial
This project demonstrates modern web development practices using Next.js 14, React, and TypeScript. It showcases:
- Component-Based Architecture: Reusable, modular components
- Type Safety: Full TypeScript implementation
- Performance: Optimized images, fonts, and animations
- User Experience: Smooth animations and interactive elements
- SEO: Comprehensive metadata and optimization
- Responsive Design: Mobile-first approach
- Best Practices: Clean code, proper file structure, and documentation
The codebase is educational and suitable for learning Next.js App Router, Framer Motion animations, and modern React patterns. All components are well-commented and can be easily adapted for other projects.
Feel free to use this project repository and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://arnob-mahmud.vercel.app/.
Enjoy building and learning! π
Thank you! π




