Pendahuluan
Landing page yang efektif dapat menjadi alat yang ampuh untuk menarik perhatian audiens dan meningkatkan konversi. Dalam artikel ini, kita akan membahas cara membuat landing page modern menggunakan Vite
sebagai build tool, ReactJS
sebagai framework UI, dan Tailwind CSS
untuk styling yang efisien.
Membuat Struktur Komponen Landing Page
Buat komponen dasar seperti LandingPage
, Home
, Footer
, About
dan OurFeatures
. Contoh sederhana untuk komponen App
adalah sebagai berikut:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import LandingPage from './LandingPage.tsx'
import './index.css'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<LandingPage />
</StrictMode>,
)
Landing Page
import React, { lazy } from "react";
import { ChevronUp } from "lucide-react";
const NavBar = lazy(() => import("./layout/NavBar"));
const Home = lazy(() => import("./page/Home"));
const OurFeatures = lazy(() => import("./page/OurFeatures"));
const AboutUs = lazy(() => import("./page/AboutUs"));
const Footer = lazy(() => import("./page/Footer"));
const LandingPage = () => {
const windowScroll = () => {
const backToTop = document.getElementById("back-to-top");
if (backToTop) {
if (document.body.scrollTop >= 50 || document.documentElement.scrollTop >= 50) {
backToTop.style.opacity = "1";
} else {
backToTop.style.opacity = "0";
}
}
};
React.useEffect(() => {
window.addEventListener('scroll', windowScroll);
window.addEventListener('load', windowScroll);
return () => {
window.removeEventListener('scroll', windowScroll);
};
}, []);
const handleScroll = () => {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
};
React.useEffect(() => {
const bodyElement = document.body;
bodyElement.classList.add("text-base", "bg-white", "text-body", "font-public");
return () => {
bodyElement.classList.remove("text-base", "bg-white", "text-body", "font-public");
}
}, []);
React.useEffect(() => {
document.documentElement.classList.add("overflow-x-hidden", "scroll-smooth", "group");
return () => {
document.documentElement.classList.remove("overflow-x-hidden", "scroll-smooth", "group");
};
}, []);
return (
<React.Fragment>
<NavBar />
<Home />
<OurFeatures />
<AboutUs />
<Footer />
<button id="back-to-top"
className="fixed flex items-center justify-center size-10 text-white bg-purple-500 rounded-md bottom-10 right-10"
onClick={handleScroll}>
<ChevronUp className="animate animate-icons"></ChevronUp>
</button>
</React.Fragment>
);
};
export default LandingPage;
Navbar
import React from "react";
import { Menu } from "lucide-react";
const NavBar = () => {
const [isToggle, setIsToggle] = React.useState<boolean>(false);
const [activeLink, setActiveLink] = React.useState<string>("/Home");
const handleLinkClick = (target: string) => {
setActiveLink(target);
};
React.useEffect(() => {
const defaultLinkElement = document.querySelector(`.navbar-menu li.active`);
if (defaultLinkElement) {
setActiveLink(defaultLinkElement.getAttribute('href') || "/Home");
}
}, [activeLink]);
const [navClass, setNavClass] = React.useState<string>('');
// scroll
const scrollNavigation = () => {
var scrollUp = document.documentElement.scrollTop;
if (scrollUp >= 50) {
setNavClass('is-sticky duration-700');
} else {
setNavClass('');
}
};
React.useEffect(() => {
window.addEventListener('scroll', scrollNavigation, true);
// Cleanup function to remove the event listener
return () => {
window.removeEventListener('scroll', scrollNavigation, true);
};
}, []);
return (
<React.Fragment>
<nav className={`fixed inset-x-0 top-0 z-50 flex items-center justify-center h-20 py-3 [&.is-sticky]:bg-white border-b border-slate-200 [&.is-sticky]:shadow-lg [&.is-sticky]:shadow-slate-200/25 navbar ${navClass}`} id="navbar">
<div className="container 2xl:max-w-[87.5rem] px-4 mx-auto flex items-center self-center w-full">
<div className="mx-auto">
<ul id="navbar7" className={`absolute inset-x-0 z-20 items-center py-3 bg-white shadow-lg
md:z-0 navbar-menu rounded-b-md md:shadow-none md:flex top-full ltr:ml-auto rtl:mr-auto md:relative md:bg-transparent md:rounded-none md:top-auto md:py-0 ${!isToggle ? "hidden" : ""}`}>
<li>
<a href="#home"
className={`block md:inline-block px-4 md:px-3 py-2.5 md:py-0.5 text-15 font-medium text-slate-800 transition-all duration-300 ease-linear hover:text-custom-500 [&.active]:text-custom-500 ${activeLink === "/Home" ? "active" : ""}`}
onClick={() => handleLinkClick("/Home")}
>
Home</a>
</li>
<li>
<a href="#features"
className={`block md:inline-block px-4 md:px-3 py-2.5 md:py-0.5 text-15 font-medium text-slate-800 transition-all duration-300 ease-linear hover:text-custom-500 [&.active]:text-custom-500 ${activeLink === "/Features" ? "active" : ""}`}
onClick={() => handleLinkClick("/Features")}
>
Layanan Kami</a>
</li>
<li>
<a href="#about"
className={`block md:inline-block px-4 md:px-3 py-2.5 md:py-0.5 text-15 font-medium text-slate-800 transition-all duration-300 ease-linear hover:text-custom-500 [&.active]:text-custom-500 ${activeLink === "/About" ? "active" : ""}`}
onClick={() => handleLinkClick("/About")}
>
Tentang Kami</a>
</li>
<li>
<a href="#contact"
className={`block md:inline-block px-4 md:px-3 py-2.5 md:py-0.5 text-15 font-medium text-slate-800 transition-all duration-300 ease-linear hover:text-custom-500 [&.active]:text-custom-500 ${activeLink === "/Contact" ? "active" : ""}`}
onClick={() => handleLinkClick("/Contact")}
>
Kontak</a>
</li>
</ul>
</div>
<div className="flex gap-2">
<div className="ltr:ml-auto rtl:mr-auto md:hidden navbar-toggale-button">
<button type="button"
className="flex items-center justify-center size-[37.5px] p-0 text-white btn bg-custom-500 border-custom-500 hover:text-white hover:bg-custom-600 hover:border-custom-600 focus:text-white focus:bg-custom-600 focus:border-custom-600 focus:ring focus:ring-custom-100 active:text-white active:bg-custom-600 active:border-custom-600 active:ring active:ring-custom-100"
onClick={() => setIsToggle(!isToggle)}
>
<Menu></Menu></button>
</div>
</div>
</div>
</nav>
</React.Fragment>
);
}
export default NavBar;
Home
import React from "react";
const Home = () => {
return (
<React.Fragment>
<section className="relative pb-36 pt-44" id="home">
<div className="container 2xl:max-w-[87.5rem] px-4 mx-auto">
<div className="grid grid-cols-12 2xl:grid-cols-2">
<div className="col-span-12 lg:col-span-7 2xl:col-span-1">
<h1 className="mb-8 !leading-relaxed md:text-5xl">Belajar membaut aplikasi <span className="relative inline-block px-2 mx-2 before:block before:absolute before:-inset-1 before:-skew-y-6 before:bg-sky-50 before:rounded-md before:backdrop-blur-xl"><span className="relative text-sky-500">Dashboard</span></span></h1>
<p className="mb-6 text-lg text-slate-500">Lorem ipsum, dolor sit amet consectetur adipisicing elit. Rem eligendi soluta animi tempora, porro eaque aliquam ea provident delectus dolorem explicabo consectetur culpa fugiat illum vitae veniam enim corrupti. Libero.</p>
</div>
</div>
</div>
</section>
</React.Fragment>
);
}
export default Home;
OurFeatures
import React from "react";
// Image
import widgets from "./../assets/card-1.png";
import widgets2 from "./../assets/card-2.png";
import widgets3 from "./../assets/chart-1.png";
import widgets4 from "./../assets/chart-2.png";
import home from "./../assets/table.png";
import achievement from "./../assets/achievement.png";
const OurFeatures = () => {
return (
<React.Fragment>
<section className="relative py-32 bg-slate-900 " id="features">
<div className="container 2xl:max-w-[87.5rem] px-4 mx-auto">
<div className="grid grid-cols-1 gap-5 xl:grid-cols-12">
<div className="order-2 xl:col-span-5 xl:order-1">
<div className="relative z-10 grid grid-cols-3 gap-5 xl:grid-cols-12">
<div className="xl:col-span-12">
<img src={widgets} alt="" className="shadow-xl rtl:mr-auto ltr:ml-auto rounded-xl" />
</div>
<div className="xl:col-span-6">
<img src={widgets3} alt="" className="shadow-xl xl:mt-5 rtl:mr-auto ltr:ml-auto rounded-xl" />
</div>
<div className="xl:col-span-6">
<img src={widgets2} alt="" className="shadow-xl xl:mt-5 rtl:mr-auto ltr:ml-auto rounded-xl" />
<img src={widgets4} alt="" className="shadow-xl xl:mt-5 rtl:mr-auto ltr:ml-auto rounded-xl mt-2" />
</div>
</div>
</div>
<div className="relative z-20 order-1 xl:-mt-72 xl:col-span-7 xl:order-last">
<img src={home} alt="" className="shadow-2xl rounded-xl" />
<img src={achievement} alt="" className="shadow-2xl rounded-xl mt-2" />
</div>
</div>
</div>
</section>
</React.Fragment>
);
}
export default OurFeatures;
AboutUs
import React from "react";
import { MoveRight } from "lucide-react";
// Image
import widgets from "./../assets/widgets.jpg";
import widgets2 from "./../assets/widgets-2.jpg";
import widgets4 from "./../assets/widgets-4.jpg";
import widgets5 from "./../assets/widgets-5.jpg";
const AboutUs = () => {
return (
<React.Fragment>
<section className="relative py-10" id="about">
<div className="container 2xl:max-w-[87.5rem] px-4 mx-auto">
<div className="grid items-center grid-cols-1 gap-6 mt-20 lg:grid-cols-12">
<div className="lg:col-span-5">
<h1 className="mb-3 leading-normal capitalize">Craft a Complete Website Quickly with the Help of Design Blocks</h1>
<p className="mb-6 text-lg text-slate-500 ">The block design approach basically breaks the design into small parts. These are built independently and then later combine into a customizable page which makes a website more flexible in terms of layout and content.</p>
<button type="button" className="py-2.5 px-6 bg-white text-custom-500 btn border-custom-500 hover:text-white hover:bg-custom-600 hover:border-custom-600 focus:text-white focus:bg-custom-600 focus:border-custom-600 focus:ring focus:ring-custom-100 active:text-white active:bg-custom-600 active:border-custom-600 active:ring active:ring-custom-100 ">
<span className="align-middle">Discover Now</span> <MoveRight className="inline-block size-4 align-middle ltr:ml-1 rtl:mr-1 rtl:rotate-180"></MoveRight></button>
</div>
<div className="text-center lg:col-span-6">
<img src={widgets4} alt="" className="shadow-lg ltr:lg:ml-auto rtl:lg:mr-auto rounded-xl" />
<img src={widgets5} alt="" className="relative -mt-24 shadow-lg ltr:ml-auto ltr:mr-24 rtl:mr-auto rtl:ml-24 rounded-xl" />
</div>
</div>
<div className="grid items-center grid-cols-1 gap-6 mt-32 lg:grid-cols-12">
<div className="text-center lg:col-span-6">
<img src={widgets2} alt="" className="shadow-lg ltr:ml-auto rtl:mr-auto rounded-xl" />
<img src={widgets} alt="" className="relative -mt-24 ml-[30%] shadow-lg ltr:ml-auto rtl:mr-auto ltr:mr-24 rtl:ml-24 rounded-xl" />
</div>
<div className="lg:col-span-5">
<h1 className="mb-3 leading-normal capitalize">Boost Your Business Using Our Potent Tools</h1>
<p className="mb-6 text-lg text-slate-500 ">The block design approach basically breaks the design into small parts. These are built independently and then later combine into a customizable page which makes a website more flexible in terms of layout and content.</p>
<button type="button" className="py-2.5 px-6 bg-white text-custom-500 btn border-custom-500 hover:text-white hover:bg-custom-600 hover:border-custom-600 focus:text-white focus:bg-custom-600 focus:border-custom-600 focus:ring focus:ring-custom-100 active:text-white active:bg-custom-600 active:border-custom-600 active:ring active:ring-custom-100 ">
<span className="align-middle">Discover Now</span> <MoveRight className="inline-block size-4 align-middle ltr:ml-1 rtl:mr-1 rtl:rotate-180"></MoveRight></button>
</div>
</div>
</div>
</section>
</React.Fragment>
);
}
export default AboutUs;
Footer
import React from "react";
import { Facebook, Instagram, Linkedin, Twitter, Youtube } from "lucide-react";
// Image
const Footer = () => {
return (
<React.Fragment>
<footer className="relative pt-20 pb-12 bg-slate-800">
<div className="container 2xl:max-w-[87.5rem] px-4 mx-auto">
<div className="relative z-10 grid grid-cols-12 gap-5 xl:grid-cols-12">
<div className="col-span-12 md:col-span-6 lg:col-span-4 xl:col-span-3">
<h5 className="mb-4 font-medium tracking-wider text-slate-50">Dashboards</h5>
<ul className="flex flex-col gap-3 text-15">
<li>
<a href="#!" className="relative inline-block transition-all duration-200 ease-linear text-slate-400 hover:text-slate-300 before:absolute before:border-b before:border-slate-500 before:inset-x-0 before:bottom-0 before:w-0 hover:before:w-full before:transition-all before:duration-300 before:ease-linear">Analytics</a>
</li>
<li>
<a href="#!" className="relative inline-block transition-all duration-200 ease-linear text-slate-400 hover:text-slate-300 before:absolute before:border-b before:border-slate-500 before:inset-x-0 before:bottom-0 before:w-0 hover:before:w-full before:transition-all before:duration-300 before:ease-linear">CRM</a>
</li>
</ul>
</div>
<div className="col-span-12 md:col-span-6 lg:col-span-4 xl:col-span-3">
<h5 className="mb-4 font-medium tracking-wider text-slate-50">Apps Pages</h5>
<ul className="flex flex-col gap-3 text-15">
<li>
<a href="#!" className="relative inline-block transition-all duration-200 ease-linear text-slate-400 hover:text-slate-300 before:absolute before:border-b before:border-slate-500 before:inset-x-0 before:bottom-0 before:w-0 hover:before:w-full before:transition-all before:duration-300 before:ease-linear">Ecommerce Apps</a>
</li>
<li>
<a href="#!" className="relative inline-block transition-all duration-200 ease-linear text-slate-400 hover:text-slate-300 before:absolute before:border-b before:border-slate-500 before:inset-x-0 before:bottom-0 before:w-0 hover:before:w-full before:transition-all before:duration-300 before:ease-linear">Invoices</a>
</li>
</ul>
</div>
<div className="col-span-12 md:col-span-6 lg:col-span-4 xl:col-span-2">
<h5 className="mb-4 font-medium tracking-wider text-slate-50">Resources</h5>
<ul className="flex flex-col gap-3 text-15">
<li>
<a href="#!" className="relative inline-block transition-all duration-200 ease-linear text-slate-400 hover:text-slate-300 before:absolute before:border-b before:border-slate-500 before:inset-x-0 before:bottom-0 before:w-0 hover:before:w-full before:transition-all before:duration-300 before:ease-linear">All Resources</a>
</li>
<li>
<a href="#!" className="relative inline-block transition-all duration-200 ease-linear text-slate-400 hover:text-slate-300 before:absolute before:border-b before:border-slate-500 before:inset-x-0 before:bottom-0 before:w-0 hover:before:w-full before:transition-all before:duration-300 before:ease-linear">Blog</a>
</li>
</ul>
</div>
<div className="col-span-12 md:col-span-6 lg:col-span-12 xl:col-span-4">
<div className="flex">
<div className="flex-1">
<p className="mb-1 text-slate-500 text-15">Support Email</p>
<h5 className="text-lg !font-normal text-slate-200">support@xxxx.com</h5>
</div>
<div className="flex-1">
<p className="mb-1 text-slate-500 text-15">Contact Us</p>
<h5 className="text-lg !font-normal text-slate-200">+(012) 1111 222 3434</h5>
</div>
</div>
</div>
</div>
<div className="py-5 mt-20 border-y border-slate-700">
<div className="relative z-10 grid grid-cols-1 gap-5 md:grid-cols-12">
<div className="md:col-span-3 md:col-start-10">
<ul className="flex items-center gap-3 md:justify-end">
<li>
<a href="#!" className="flex items-center justify-center size-10 transition-all duration-200 ease-linear border rounded-full text-slate-400 border-slate-700 hover:text-custom-500">
<Facebook className="size-4"></Facebook></a>
</li>
<li>
<a href="#!" className="flex items-center justify-center size-10 transition-all duration-200 ease-linear border rounded-full text-slate-400 border-slate-700 hover:text-custom-500">
<Linkedin className="size-4"></Linkedin></a>
</li>
<li>
<a href="#!" className="flex items-center justify-center size-10 transition-all duration-200 ease-linear border rounded-full text-slate-400 border-slate-700 hover:text-custom-500">
<Instagram className="size-4"></Instagram></a>
</li>
<li>
<a href="#!" className="flex items-center justify-center size-10 transition-all duration-200 ease-linear border rounded-full text-slate-400 border-slate-700 hover:text-custom-500">
<Twitter className="size-4"></Twitter></a>
</li>
<li>
<a href="#!" className="flex items-center justify-center size-10 transition-all duration-200 ease-linear border rounded-full text-slate-400 border-slate-700 hover:text-custom-500">
<Youtube className="size-4"></Youtube></a>
</li>
</ul>
</div>
</div>
</div>
<div className="mt-12 text-center text-slate-400 text-16">
<p>
{new Date().getFullYear()} © ramadhan. Design & Develop by
</p>
</div>
</div>
</footer>
</React.Fragment>
);
}
export default Footer;
Top comments (0)