Shadcnspace Builder
blockhero-02source: ok · refreshed
Hero 02 - Real Estate Hero Section
Luxury real estate hero section crafted for property listings, featuring immersive imagery, location highlight, key amenities, pricing details, and prominent Schedule a tour CTA for faster inquiries.
npx shadcn add "https://builder.shadcnspace.com/r/hero-02.json"free
Where it goes
heropricingctastats
Interaction
animated-counter
Use cases
saas-landing
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · mediumiconslucide-react
animationmotion
Registry dependencies
sheetbuttonseparator
Files6 · source cached 10/2/2026, 1:26:53 AM
src/components/shadcn-space/blocks/hero-02/page.tsx
import Hero04Page from ".";
const Page = () => {
return (
<Hero04Page />
)
}
export default Pagesrc/components/shadcn-space/blocks/hero-02/hero.tsx
"use client";
import { useRef } from "react";
import { Button } from "@/components/ui/button";
import SeamlessCloud from "@/components/shadcn-space/blocks/hero-02/seamless-cloud";
import { Bath, BedDouble, Car } from "lucide-react";
import { Separator } from "@/components/ui/separator";
import { motion, useInView } from "motion/react";
const propertyFeatures = [
{
icon: BedDouble,
label: "4 Bedrooms",
className: "border-e border-b",
},
{
icon: Bath,
label: "4 Restroom",
className: "border-b",
},
{
icon: Car,
label: "Parking space",
className: "border-e",
},
{
price: "$4,750,000",
label: "For selling price",
className: "",
},
];
const HeroSection: React.FC = () => {
const sectionRef = useRef<HTMLElement>(null);
const isInView = useInView(sectionRef, { once: true, amount: 0.1 });
return (
<section ref={sectionRef}>
<div className='bg-[url(https://images.shadcnspace.com/assets/backgrounds/real-estate-bg.webp)] bg-contain bg-center bg-repeat overflow-hidden relative flex flex-col xl:h-screen justify-center z-10 xl:gap-0 gap-12'>
<div className='max-w-7xl mx-auto sm:px-16 px-4 w-full xl:pt-0 pt-32'>
<motion.div
initial={{ opacity: 0, x: -40 }}
animate={isInView ? { opacity: 1, x: 0 } : { opacity: 0, x: -40 }}
transition={{ duration: 0.7, ease: "easeInOut" }}
className='relative text-white text-start z-30'
>
<p className='text-inherit text-xs font-normal'>Palm springs, CA</p>
<h1 className='text-inherit text-5xl! md:text-6xl! lg:text-7xl! font-normal! max-w-32 mt-2 mb-6'>
Space <span className='font-semibold!'>Residence</span>
</h1>
<div>
<Button
className='px-6 py-3.5 bg-white border-0 text-black duration-300 hover:bg-white/80 font-medium rounded-full hover:cursor-pointer h-auto'>
<a href='#'>Schedule a tour</a>
</Button>
</div>
</motion.div>
</div>
<div className="xl:absolute bottom-0 right-0 z-30 xl:w-auto lg:w-4/5 w-full lg:ms-auto">
<div className='relative'>
<div className='xl:absolute bottom-24 w-full z-0'>
<img src={'https://images.shadcnspace.com/assets/backgrounds/hero-4-banner.webp'} alt='heroImg' width={956} height={897} className='w-full' />
</div>
<div className='bg-background rounded-t-2xl xl:rounded-none xl:rounded-tl-2xl sm:py-10 py-6 sm:ps-12 ps-4 sm:pe-12 pe-4 xl:pe-60 z-1 relative'>
<motion.div
initial={{ opacity: 0, y: 40 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 40 }}
transition={{ duration: 1, ease: "easeInOut" }}
className='grid grid-cols-2 sm:grid-cols-4 gap-0 sm:flex sm:items-center justify-center sm:gap-10 sm:text-center'
>
{propertyFeatures.map((item, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 32 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 32 }}
transition={{
duration: 1,
delay: 0.1 + index * 0.2,
ease: "easeInOut",
}}
className="flex sm:gap-10"
>
<div className={`flex flex-col items-center gap-3 sm:py-0 sm:px-0 py-5 px-8 sm:border-0 border-gray-200 dark:border-gray-700 w-full ${item.className}`}>
{item.icon ? (
<>
<item.icon size={28} className='text-foreground font-light' />
<p className='text-sm font-normal text-muted-foreground'>
{item.label}
</p>
</>
) : (
<>
<p className='sm:text-xl text-lg font-semibold text-foreground'>
{item.price}
</p>
<p className='text-sm font-normal text-muted-foreground'>
{item.label}
</p>
</>
)}
</div>
{index < propertyFeatures.length - 1 && (
<Separator orientation="vertical" className="h-12 my-auto sm:block hidden" />
)}
</motion.div>
))}
</motion.div>
</div>
</div>
</div>
{/* Clouds */}
<>
<SeamlessCloud
cloudCount={2}
minSize={400}
maxSize={678}
opacity="opacity-60"
gapMin={100}
gapMax={500}
top="top-56 sm:top-40 left-0"
/>
</>
</div>
</section>
);
};
export default HeroSection;
src/components/shadcn-space/blocks/hero-02/seamless-cloud.tsx
'use client';
import { Marquee } from '@/components/shadcn-space/animations/marquee';
import { useEffect, useState } from 'react';
export default function SeamlessCloud({
top = "top-72 sm:top-20",
cloudCount = 5,
minSize = 400,
maxSize = 678,
opacity = "opacity-60",
gapMin = 50,
gapMax = 200,
}) {
const [clouds, setClouds] = useState<{ width: number; gap: number }[]>([]);
useEffect(() => {
const generated = Array.from({ length: cloudCount }).map(() => ({
width: Math.floor(Math.random() * (maxSize - minSize + 1)) + minSize,
gap: Math.floor(Math.random() * (gapMax - gapMin + 1)) + gapMin,
}));
setClouds(generated);
}, [cloudCount, minSize, maxSize, gapMin, gapMax]);
if (clouds.length === 0) return null; // prevents mismatch
return (
<div
className={`absolute ${top} inset-x-0 z-0 overflow-hidden pointer-events-none`}
>
<Marquee pauseOnHover className="[--duration:30s]">
{clouds.map((cloud, i) => (
<img
key={i}
src="https://images.shadcnspace.com/assets/backgrounds/cloud.webp"
alt="cloud"
width={cloud.width}
height={350}
className={opacity}
style={{ marginRight: cloud.gap }}
/>
))}
</Marquee>
</div>
);
}
src/components/shadcn-space/blocks/hero-02/navbar.tsx
"use client";
import React, { useEffect, useState, useCallback, useRef } from 'react'
import { cn } from '@/lib/utils';
import {
Sheet,
SheetContent,
SheetClose,
SheetHeader,
} from '@/components/ui/sheet'
import { Button } from '@/components/ui/button'
import { Menu, Phone, X } from 'lucide-react'
import { Separator } from '@/components/ui/separator';
import { motion, useInView } from "motion/react";
type navData = {
name: string;
href: string;
};
type NavbarProps = {
navData: navData[]
}
export type NavLinkItem = {
name: string
label?: string
href: string
isActive?: boolean
}
export interface NavLinkProps {
item: NavLinkItem
}
const NavLink: React.FC<NavLinkProps> = ({
item,
}) => {
return (
<li className="flex items-center group w-fit cursor-pointer">
<div className={`h-0.5 bg-primary transition-all duration-300 ${item.isActive ? 'w-6 mr-4' : 'w-0 group-hover:w-6 group-hover:mr-4'}`} />
<a
href={item.href}
className={`text-2xl font-medium rounded-full transition-colors ${item.isActive ? 'text-primary' : "text-muted-foreground group-hover:text-primary"}`}
>
{item.name}
</a>
</li>
)
}
const Navbar: React.FC<NavbarProps> = ({ navData }) => {
const [sticky, setSticky] = useState(false);
const [navbarOpen, setNavbarOpen] = useState(false);
const headerRef = useRef<HTMLElement>(null);
const isInView = useInView(headerRef, { once: true, amount: 0.1 });
const handleScroll = useCallback(() => {
setSticky(window.scrollY >= 50);
}, []);
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, [handleScroll]);
return (
<motion.header
ref={headerRef}
initial={{ opacity: 0, y: -32 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: -32 }}
transition={{ duration: 0.7, ease: "easeInOut" }}
className={cn(
"z-50 w-full bg-transparent h-20 sticky top-0 flex items-center"
)}
>
<nav
className={`max-w-7xl mx-auto sm:px-16 px-4 w-full`}>
<div className={`flex items-center justify-between duration-300 ${sticky
? 'shadow-lg bg-background rounded-full p-3'
: 'shadow-none px-0'
}`}>
<div className='flex justify-between items-center gap-2 w-full'>
<div>
<a href='#'>
<img
src={
'https://images.shadcnspace.com/assets/logo/shadcnspace-logo-black.svg'
}
alt='logo'
width={150}
height={68}
className={`${sticky ? 'block dark:hidden' : 'hidden'}`}
/>
<img
src={
'https://images.shadcnspace.com/assets/logo/shadcnspace-logo-white.svg'
}
alt='logo'
width={150}
height={68}
className={`${sticky ? 'hidden dark:block' : 'block'}`}
/>
</a>
</div>
<div className='flex items-center gap-2 sm:gap-6'>
<div className={`hidden md:block`}>
<a
href='tel:+1212456789'
className={`text-sm text-inherit flex items-center gap-2 ${sticky
? 'text-foreground hover:text-primary border-white/50 dark:border-white/50'
: 'text-white hover:text-white/80 border-white/50 dark:border-white/50'
}`}>
<Phone size={20} />
+1-212-456-789
</a>
</div>
<Separator orientation="vertical" className={`h-5 my-auto sm:block hidden ${sticky ? "bg-white/50 dark:bg-white/50" : "bg-white/50 dark:bg-white/50"}`} />
<div>
<Button
size={"lg"}
onClick={() => setNavbarOpen(!navbarOpen)}
className={`flex items-center gap-3 px-5 py-2 rounded-full font-normal hover:cursor-pointer border ${sticky
? 'text-dark dark:text-white bg-background dark:hover:text-white dark:hover:bg-background hover:text-dark hover:bg-white border-dark dark:border-white'
: 'text-black bg-white dark:text-dark hover:bg-transparent hover:text-white border-white'
}`}
aria-label='Toggle mobile menu'>
<Menu size={16} />
Menu
</Button>
</div>
</div>
</div>
</div>
</nav>
<Sheet open={navbarOpen} onOpenChange={setNavbarOpen}>
<SheetContent
side="right"
showCloseButton={false}
className="bg-background p-6 overflow-auto no-scrollbar border-none flex flex-col gap-12"
>
{/* Header / Close Button */}
<SheetHeader>
<SheetClose className='absolute top-4 right-4 rounded-full dark:bg-white bg-black dark:text-black text-white p-2'>
<X size={24} />
</SheetClose>
</SheetHeader>
{/* Navigation */}
<nav>
<ul className='flex flex-col gap-4'>
{navData?.map((item, index) => (
<SheetClose key={index} className="w-fit">
<NavLink item={item} />
</SheetClose>
))}
</ul>
</nav>
{/* Contact & Socials */}
<div className="flex flex-col gap-12 text-foreground">
{/* Contact */}
<div className="flex flex-col gap-1">
<p className="text-lg font-normal text-muted-foreground">
Contact
</p>
<a href="#" className="text-base font-medium hover:text-primary">
hello@homely.com
</a>
<a href="#" className="text-base font-medium hover:text-primary">
+1-212-456-7890
</a>
</div>
{/* Socials */}
<div className="flex flex-col gap-1">
<p className="text-lg font-normal text-muted-foreground">
Socials
</p>
<a href="#" className="text-base font-medium hover:text-primary">
X / Twitter
</a>
<a href="#" className="text-base font-medium hover:text-primary">
Facebook
</a>
<a href="#" className="text-base font-medium hover:text-primary">
Instagram
</a>
</div>
</div>
</SheetContent>
</Sheet>
</motion.header>
);
};
export default Navbar
src/components/shadcn-space/animations/marquee.tsx
"use client";
import { ComponentPropsWithoutRef } from "react";
import { cn } from "@/lib/utils";
interface MarqueeProps extends ComponentPropsWithoutRef<"div"> {
/**
* Optional CSS class name to apply custom styles
*/
className?: string;
/**
* Whether to reverse the animation direction
* @default false
*/
reverse?: boolean;
/**
* Whether to pause the animation on hover
* @default false
*/
pauseOnHover?: boolean;
/**
* Content to be displayed in the marquee
*/
children: React.ReactNode;
/**
* Whether to animate vertically instead of horizontally
* @default false
*/
vertical?: boolean;
/**
* Number of times to repeat the content
* @default 4
*/
repeat?: number;
}
export function Marquee({
className,
reverse = false,
pauseOnHover = false,
children,
vertical = false,
repeat = 4,
...props
}: MarqueeProps) {
return (
<>
<style>{`
@keyframes marquee {
from { transform: translateX(0); }
to { transform: translateX(calc(-100% - var(--gap))); }
}
@keyframes marquee-vertical {
from { transform: translateY(0); }
to { transform: translateY(calc(-100% - var(--gap))); }
}
.animate-marquee {
animation: marquee var(--duration) linear infinite;
}
.animate-marquee-vertical {
animation: marquee-vertical var(--duration) linear infinite;
}
`}</style>
<div
{...props}
className={cn(
"group flex [gap:var(--gap)] overflow-hidden p-2 [--duration:40s] [--gap:1rem]",
{
"flex-row": !vertical,
"flex-col": vertical,
},
className
)}
>
{Array(repeat)
.fill(0)
.map((_, i) => (
<div
key={i}
className={cn("flex shrink-0 justify-around [gap:var(--gap)]", {
"animate-marquee": !vertical,
"animate-marquee-vertical": vertical,
"group-hover:[animation-play-state:paused]": pauseOnHover,
"[animation-direction:reverse]": reverse,
})}
>
{children}
</div>
))}
</div>
</>
);
}
src/components/shadcn-space/blocks/hero-02/index.tsx
import HeroSection from "@/components/shadcn-space/blocks/hero-02/hero"
import Navbar from "@/components/shadcn-space/blocks/hero-02/navbar";
import { NavLinkItem } from "@/components/shadcn-space/blocks/hero-02/navbar";
const Hero04Page = () => {
const navData:NavLinkItem[] = [
{ name: 'Home', href: '#',isActive: true },
{ name: 'Properties', href: '#' ,isActive: false},
{ name: 'Blog', href: '#' ,isActive: false},
{ name: 'Contact', href: '#' ,isActive: false},
{ name: 'Docs', href: '#' ,isActive: false},
]
return (
<>
<Navbar navData={navData} />
<main className="-mt-20">
<HeroSection />
</main>
</>
)
}
export default Hero04PageMore from Shadcnspace Builder
About Us 01 - Impact MetricsblockAccordion-01 - DefaultcomponentAccordion-02 - Multi-levelcomponentAnimated Text-01 - Shiny TextcomponentAnimated Text-02 - Gradient TextcomponentAnimated Text-03 - TypingcomponentAnimated Text-04 - Rolling TextcomponentAnimated Text-05 - Shimmer Textcomponent
Browse all components in this library →