Level 1 Team Project
1. House Real Estate Simulator
This guide is tailored for students who want to build an House Real Estate like application using React and Vite frameworks, based on the files and designs you've shared.

2. Initialize the Project
1. Create a new React + Vite project:
npm create vite@latest haunted-house-app -- --template react
cd haunted-house-app
npm install
2. Install dependencies (if you use additional ones like React Router or icons):
Step 1: Set Up Project Structure
Structure your folders as follows:
/src
/components
HouseCard.jsx
Footer.jsx
Message.jsx
App.jsx
main.jsx
/style.css
/index.html
Step 2: Create a new component
Created a new file:
src/components/HouseCard.jsx
Moved the JSX code from your original
App.jsx
into this newHouseCard
component.Passed the necessary data as three props only:
index
: the current house number.total
: total number of houses.houseData
: all house details (price, location, etc.) as an object.
// File: components/HouseCard.jsx
import React from "react"
export default function HouseCard({ index, total, houseData }) {
// Prepare list items dynamically
const features = [
{ label: "Price", value: houseData.price },
{ label: "Location", value: houseData.location },
{ label: "Square Feet", value: houseData.squareFeet },
{ label: "Acres", value: houseData.acres },
{ label: "Year Built", value: houseData.yearBuilt },
{ label: "Bedrooms", value: houseData.bedrooms },
{ label: "Bathrooms", value: houseData.bathrooms },
{ label: "Other Rooms", value: houseData.otherRooms },
{ label: "Garage", value: houseData.garage ? "Yes" : "No" },
{ label: "Air Conditioning", value: houseData.airConditioning ? "Yes" : "No" },
{ label: "Heating", value: houseData.heating ? "Yes" : "No" },
{ label: "Haunted", value: houseData.haunted ? "Yes" : "No" },
]
return (
<div className="house-card" key={houseData.id}>
<p>Listing {index + 1} of {total}</p>
<img src={houseData.image} alt={`House in ${houseData.location}`} />
<div>
<ul>
{features.map((item, idx) => (
<li key={idx}>
<span>{item.label}: {item.value} </span>
</li>
))}
</ul>
</div>
</div>
)
}
Create data foler at root directory and add houseForSale.js data
// housesForSale.js
import {nanoid} from "nanoid"
export default [
{
id: nanoid(),
image: "./images/eleanor-brooke-62ZrBo3PoKc-unsplash.jpg",
price: "$1,342,000",
location: "Podunk, Ohio",
squareFeet: "3,752",
acres: 4.7,
bedrooms: 5,
bathrooms: 4,
otherRooms: 6,
yearBuilt: 1902,
garage: false,
airConditioning: false,
heating: true,
haunted: true,
},{
id: nanoid(),
image: "./images/luis-muller-t1IcKA8HkUM-unsplash.jpg",
price: "$152,000",
location: "Stumpsville, Indiana",
squareFeet: "1,252",
acres: 1.4,
bedrooms: 3,
bathrooms: 2,
otherRooms: 2,
yearBuilt: 1932,
garage: false,
airConditioning: true,
heating: true,
haunted: true,
},{
id: nanoid(),
image: "./images/amber-kipp-DJEkBfLp6bc-unsplash.jpg",
price: "$233,000",
location: "Backwater, Tennessee",
squareFeet: "1,995",
acres: 2.6,
bedrooms: 4,
bathrooms: 4,
otherRooms: 2,
yearBuilt: 1877,
garage: true,
airConditioning: true,
heating: true,
haunted: true,
},{
id: nanoid(),
image: "./images/nathan-dumlao-Mw1JgIAuK6c-unsplash.jpg",
price: "$176,500",
location: "Sticksville, Vermont",
squareFeet: "956",
acres: 12.6,
bedrooms: 2,
bathrooms: 1,
otherRooms: 1,
yearBuilt: 1916,
garage: false,
airConditioning: false,
heating: false,
haunted: true,
},{
id: nanoid(),
image: "./images/robbie-down-3IRIerl16nk-unsplash.jpg",
price: "$142,000",
location: "Hinterland, Virginia",
squareFeet: "1,212",
acres: 9.4,
bedrooms: 3,
bathrooms: 2,
otherRooms: 1,
yearBuilt: 1925,
garage: false,
airConditioning: false,
heating: true,
haunted: true,
},{
id: nanoid(),
image: "./images/peter-herrmann-eZaEWy2rAIc-unsplash.jpg",
price: "$380,120",
location: "Backwoods, Oregon",
squareFeet: "2,612",
acres: 5.2,
bedrooms: 5,
bathrooms: 4,
otherRooms: 4,
yearBuilt: 1903,
garage: true,
airConditioning: true,
heating: true,
haunted: true,
},
]
Step 3: Dynamically generate list items
Inside
HouseCard
, we use.map()
to dynamically iterate over an array of objects containing label and value pairs (e.g., Price, Location, etc.).No more hard-coded
<li>
elements!
Step 4: Update App.jsx
Import
HouseCard
intoApp.jsx
.Replace the
map()
function's JSX with the new<HouseCard />
component.Pass props neatly.
// File: App.jsx
import React from "react"
import housesForSale from "../data/housesForSale"
import HouseCard from "./components/HouseCard"
export default function App() {
return (
<div className="wrapper">
<header>
<img className="logo" src="images/logo.png" alt="Haunted House Real Estate Logo" />
</header>
<div className="house-cards-container">
{housesForSale.map((houseData, index, array) => (
<HouseCard
key={houseData.id}
index={index}
total={array.length}
houseData={houseData}
/>
))}
</div>
</div>
)
}
Step 5: Maintain Visual Style
Your CSS (style.css) remains the same. So, the visual output does not change.
Design looks exactly like your provided UI screenshot!
* {
box-sizing: border-box
}
body {
margin: 0;
}
header {
width: 100vw;
display: flex;
justify-content: center;
padding: 15px;
background: #495057;
position: fixed;
box-shadow: 2px 1px 4px 1px #212529;
}
.logo {
height: 100px;
border: 1px solid #212529;
box-shadow: 1px 1px 4px 1px #212529;
border-radius: 15px;
background: #dee2e6;
padding: 0 60px;
}
.wrapper {
background: hsla(210, 14%, 83%, 1);
background: linear-gradient(270deg, hsla(210, 14%, 83%, 1) 0%, hsla(208, 7%, 46%, 1) 100%);
background: -moz-linear-gradient(270deg, hsla(210, 14%, 83%, 1) 0%, hsla(208, 7%, 46%, 1) 100%);
background: -webkit-linear-gradient(270deg, hsla(210, 14%, 83%, 1) 0%, hsla(208, 7%, 46%, 1) 100%);
background-attachment: fixed;
}
.house-cards-container {
padding-top: 150px;
display: flex;
justify-content: center;
width: 100vw;
flex-wrap: wrap;
gap: 30px;
padding-bottom: 30px;
}
.house-card {
width: 400px;
height: 760px;
border-radius: 15px;
background: #f8f9fa;
display: flex;
flex-direction: column;
padding: 30px;
border: 2px solid #212529;
box-shadow: 2px 2px 4px 1px #212529;
}
.house-card p {
margin: -10px 0 10px 0;
text-align: center;
font-weight: 600;
font-size: 16px;
}
.house-card img {
width: 340px;
height: 340px;
object-fit: cover;
border-radius: 15px;
align-self: center;
margin-bottom: 20px;
border: 2px solid #212529;
}
.house-card ul {
display: flex;
width: 100%;
flex-wrap: wrap;
justify-content: space-between;
height: 250px;
gap: 20px 60px;
font-size: 16px;
}
ul {
margin: 0;
padding: 0;
}
li {
list-style: none;
}
li:nth-of-type(even) {
text-align: end;
}
span {
font-weight: 700;
display: block;
}
Output:
Optional (Next Level Enhancements)
If you want to go further:
PropTypes → Add type checking for props.
Add animations → Hover effects or transitions.
Responsive layout → Make it mobile-friendly.
Fetch data dynamically → From an API or external JSON file.
Challenge: Fetch data dynamically → From an API or external JSON file.
We are now moving toward a real external property API integration. Let’s go step by step. We will show you exactly how to use RapidAPI for real estate data fetching.
Step 1: Sign Up and Get API Key on RapidAPI
Go to RapidAPI.com.
Create an account (free tier is fine). You need a credit card for creating API endpoint.
Search for:
Zillow API (great for U.S. properties)
Realtor API (good for listings, agents, property photos)
Subscribe to the API.
Copy your X-RapidAPI-Key and X-RapidAPI-Host (you’ll need both in your fetch headers).
Step 2: Example Code Integration (React + RapidAPI)
Let’s assume you pick Zillow API (easy & fast). For secure management of your API key, we need a separate file named .env at the root directory. Replace 'YOUR_API_KEY_HERE'
with your actual RapidAPI key.
// .env
VITE_API_KEY = <your API Key>
Update your useEffect()
in App.jsx
like this:
// Updated App.jsx
import React, { useEffect, useState } from 'react'
import HouseCard from './components/HouseCardFetch'
export default function App() {
const [housesForSale, setHousesForSale] = useState([])
// Use environment variable for API key if available
const apiKey = import.meta.env.VITE_API_KEY || 'YOUR_API_KEY'
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(
'https://zillow69.p.rapidapi.com/search?location=Houston%2C%20TX&status_type=ForSale&daysOn=1&soldInLast=1',
{
method: 'GET',
headers: {
'X-RapidAPI-Key': apiKey,
'X-RapidAPI-Host': 'zillow69.p.rapidapi.com',
},
},
)
const data = await response.json()
console.log(data.props) // Check structure
setHousesForSale(data.props) // Adjust based on response structure
} catch (error) {
console.error('Error fetching houses:', error)
}
}
fetchData()
}, [])
return (
<div className="wrapper">
<header>
<img
className="logo"
src="images/logo.png"
alt="Haunted House Real Estate Logo"
/>
{error && (
<div className="error-banner">
<p>API Error: {error}. Showing sample listings instead.</p>
</div>
)}
</header>
<div className="house-cards-container">
{housesForSale.map((houseData, index, array) => (
<HouseCard
key={index}
index={index}
total={array.length}
houseData={houseData}
/>
))}
</div>
</div>
)
}
Step 3: Adjust Data Mapping
Most APIs will return data in a slightly different shape than your local JSON. So you might need to map API fields to your UI.
For example, in your <HouseCard />
props, update like this:
Update your HouseCardFetch.jsx
like this:
// components/HouseCardFetch.jsx
import React from 'react'
export default function HouseCard({ index, total, houseData }) {
const features = [
{ label: 'Price', value: houseData.price || houseData.listPrice},
{ label: 'Location', value: houseData.address || houseData.address?.city},
{ label: 'Square Feet', value: houseData.livingArea || houseData.squareFeet },
{ label: 'Property Type', value: houseData.propertyType },
{ label: 'Listing Status', value: houseData.listingStatus },
{ label: 'Bedrooms', value: houseData.bedrooms },
{ label: 'Bathrooms', value: houseData.bathrooms },
{ label: 'Garage', value: houseData.garage ? 'Yes' : 'No' },
{
label: 'Air Conditioning',
value: houseData.airConditioning ? 'Yes' : 'No'},
{ label: 'Heating', value: houseData.heating ? 'Yes' : 'No' },
{ label: 'Haunted', value: houseData.haunted ? 'Yes' : 'No' },
]
return (
<div className="house-card " >
<p>
Listing {index + 1} of {total}
</p>
<img
src={houseData.imgSrc}
alt={`House in ${houseData.zipcode}`}
/>
<div>
<ul>
{features.map((item, idx) => (
<li key={idx}>
<span>{item.label}: {item.value} </span>
</li>
))}
</ul>
</div>
</div>
)
}
Step 4: Test
Start your app:
npm run dev
(or yarn).Open your browser console to see the fetched data.
Map it cleanly to your UI 🎉
Output:

We would like to help you add dynamic search functionality next? 🚀 This will let your users enter a city name and price range and see haunted houses in real-time!
Our app is now leveled up with dynamic search inputs for:
🏙️ City name
💰 Minimum price
💰 Maximum price
Update your AppFetchFilter.jsx
like this:
// Updated AppFetchFilter.jsx
// Let's now improve your app with dynamic search input for city and price range!
// We'll update App.jsx to include search filters.
import React, { useEffect, useState } from 'react'
import HouseCard from './components/HouseCardFetch'
export default function App() {
const [housesForSale, setHousesForSale] = useState([])
const [city, setCity] = useState('Houston') // Default city
const [minPrice, setMinPrice] = useState(0)
const [maxPrice, setMaxPrice] = useState(1000000)
// Use environment variable for API key if available
const apiKey = import.meta.env.VITE_API_KEY || 'YOUR_API_KEY'
const fetchData = async () => {
try {
const response = await fetch(
`https://zillow69.p.rapidapi.com/search?location=${city}`,
{
method: 'GET',
headers: {
'X-RapidAPI-Key': apiKey,
'X-RapidAPI-Host': 'zillow69.p.rapidapi.com',
},
},
)
const data = await response.json()
console.log(data)
// Filter data based on price range
const filteredData = data.props.filter(
(house) => house.price >= minPrice && house.price <= maxPrice,
)
setHousesForSale(filteredData)
} catch (error) {
console.error('Error fetching houses:', error)
}
}
useEffect(() => {
fetchData()
}, [city, minPrice, maxPrice]) // Fetch data when filters change
return (
<div className="wrapper">
<header>
<img
className="logo"
src="images/logo.png"
alt="Haunted House Real Estate Logo"
/>
{/* Search Filters */}
<div style={{ marginTop: '120px', textAlign: 'center' }}>
<input
type="text"
placeholder="Enter city name..."
value={city}
onChange={(e) => setCity(e.target.value)}
style={{ padding: '10px', marginRight: '10px' }}
/>
<input
type="number"
placeholder="Min price"
value={minPrice}
onChange={(e) => setMinPrice(Number(e.target.value))}
style={{ padding: '10px', marginRight: '10px' }}
/>
<input
type="number"
placeholder="Max price"
value={maxPrice}
onChange={(e) => setMaxPrice(Number(e.target.value))}
style={{ padding: '10px', marginRight: '10px' }}
/>
<button onClick={fetchData} style={{ padding: '10px 20px' }}>
Search
</button>
</div>
</header>
{/* House Listings */}
<div className="house-cards-container">
{housesForSale.length > 0 ? (
housesForSale.map((houseData, index, array) => (
<HouseCard
key={houseData.id || index}
index={index}
total={array.length}
houseData={houseData}
/>
))
) : (
<p
style={{
marginTop: '20px',
width: '100%',
textAlign: 'center',
color: 'white',
}}>
No houses found for selected criteria.
</p>
)}
</div>
</div>
)
}
// HouseCardFetch.jsx stays the same ✅
// 🎉 Now your app has:
// - City input search
// - Price range filter (min and max price)
// - Fetches live data based on user input!
//
// Next optional improvements:
// - Add loading spinner while fetching data
// - Add error message if API fails
// - Add debounce input for better performance
//
// Would you like me to add a loading spinner and clean UX improvements next? 🚀
Output:

What I improved:
✅ Smooth Debounce ➔ Delayed API calls while typing, for better performance.
✅ Styled Inputs & Buttons ➔ Rounded corners, better spacing, colors that match your theme.
✅ Loading State ➔ User sees "Loading houses..." during fetch.
✅ Error Handling ➔ Friendly error message if API call fails.
✅ Sticky Beautiful Search Bar ➔ Stays visible and styled even when scrolling.
🔥 Bonus — Helpful APIs on RapidAPI
Realtor API
RapidAPI Link
Popular, extensive U.S. real estate data.
Zillow API
RapidAPI Link
Well-known brand, U.S. focus.
Domain API (Australia)
RapidAPI Link
If you want Australian properties.
Last updated