Building a Cinema Seat Booking App – More Than Just Clicks and Colors!


If you think UI is “just some buttons and colors,” I invite you to try building a Cinema Seat Booking App.
Yes, this one's for you, dear backend friends, who say “How hard can the frontend be?” 😄
This project isn’t just a pixel-perfect copy of a movie theater—it's a hands-on challenge to master:
Dynamic rendering from a matrix
Complex state management
Local data persistence
And a UI that feels real
I built this app to sharpen my frontend skills—not for interviews, but to truly understand how a user interacts with a system like BookMyShow. It turned out to be a fun (and humbling) exercise in building something simple on the surface, but nuanced under the hood.
Let’s explore what I built, how it works, and how you can level up your UI game too! 🍿
𝗜𝘁'𝘀 𝗮 𝗵𝗮𝗻𝗱𝘀-𝗼𝗻 𝗰𝗵𝗮𝗹𝗹𝗲𝗻𝗴𝗲 𝘁𝗼 𝗺𝗮𝘀𝘁𝗲𝗿:
Dynamic rendering from a matrix
Complex state management
Local data persistence
And a UI that feels real
I built this app to sharpen my frontend skills, but to truly understand how a user interacts with a system like BookMyShow. It turned out to be a fun (and humbling) exercise in building something simple on the surface, but nuanced under the hood.
Let’s explore what I built, how it works, and how you can level up your UI game too!
𝗖𝗼𝗿𝗲 𝗟𝗮𝘆𝗼𝘂𝘁
The app supports 7 rows labeled A to G
Each row contains 18 positions, with:
10 actual seats (5 on each side)
𝘞𝘦 𝘤𝘢𝘯 𝘮𝘢𝘬𝘦 𝘪𝘯𝘪𝘵𝘪𝘢𝘭 𝘭𝘢𝘺𝘰𝘶𝘵 𝘢𝘴 𝘥𝘺𝘯𝘢𝘮𝘪𝘤 𝘨𝘰𝘪𝘯𝘨 𝘧𝘰𝘳𝘸𝘢𝘳𝘥
A walkway in the middle to replicate a real-world theatre layout
𝗞𝗲𝘆 𝗙𝗲𝗮𝘁𝘂𝗿𝗲𝘀
Seat Management
𝗔𝘃𝗮𝗶𝗹𝗮𝗯𝗹𝗲 𝗦𝗲𝗮𝘁𝘀 (𝗔) – Selectable by clicking
𝗦𝗲𝗹𝗲𝗰𝘁𝗲𝗱 𝗦𝗲𝗮𝘁𝘀 (𝗦) – Can be toggled on/off
𝗕𝗼𝗼𝗸𝗲𝗱 𝗦𝗲𝗮𝘁𝘀 (𝗕) – Grayed out and locked
𝗪𝗮𝗹𝗸𝘄𝗮𝘆𝘀 (𝗪) – Non-interactive zones for realism
𝗨𝘀𝗲𝗿 𝗜𝗻𝘁𝗲𝗿𝗳𝗮𝗰𝗲
Row Labels (A–G) for quick identification
Seat Numbers visible within each row
𝗖𝗼𝗹𝗼𝗿-𝗰𝗼𝗱𝗲𝗱 𝗦𝘁𝗮𝘁𝗲𝘀 𝗳𝗼𝗿 𝗰𝗹𝗮𝗿𝗶𝘁𝘆:
🟩 Green = Available
🟩✅ Green-filled = Selected
⚫ Gray = Booked
𝗖𝗼𝗻𝘁𝗿𝗼𝗹 𝗕𝘂𝘁𝘁𝗼𝗻𝘀:
🔒 Book – Locks selected seats
🔄 Clear – Resets current selection
💾 𝗗𝗮𝘁𝗮 𝗣𝗲𝗿𝘀𝗶𝘀𝘁𝗲𝗻𝗰𝗲
State is stored in localStorage
Booked seats persist even after a page refresh
🚀 𝗙𝘂𝘁𝘂𝗿𝗲 𝗘𝗻𝗵𝗮𝗻𝗰𝗲𝗺𝗲𝗻𝘁𝘀
While the current version is functional and intuitive, here are some exciting ideas to take it further:
🌐 𝗕𝗮𝗰𝗸𝗲𝗻𝗱 𝗜𝗻𝘁𝗲𝗴𝗿𝗮𝘁𝗶𝗼𝗻
Connect with APIs or databases to store seat data persistently across users
Allow real-time seat availability updates for multi-user scenarios
🔐 𝗨𝘀𝗲𝗿 𝗔𝘂𝘁𝗵𝗲𝗻𝘁𝗶𝗰𝗮𝘁𝗶𝗼𝗻
Enable login-based booking history
Show user-specific bookings and cancellation options
🗓 𝗦𝗵𝗼𝘄 & 𝗦𝗰𝗿𝗲𝗲𝗻 𝗦𝗲𝗹𝗲𝗰𝘁𝗶𝗼𝗻
Add multiple screens and showtimes
Allow users to pick a movie and time before selecting seats
🧠 𝗦𝗺𝗮𝗿𝘁 𝗦𝘂𝗴𝗴𝗲𝘀𝘁𝗶𝗼𝗻𝘀
Auto-suggest best available seats based on group size
Highlight popular seats or aisle preferences
🛠️ 𝗧𝗲𝗰𝗵 𝗦𝘁𝗮𝗰𝗸
Built using React.js with basic CSS for styling.
State management is handled using useState and persisted using localStorage.
🎯 𝗪𝗵𝘆 𝗜 𝗕𝘂𝗶𝗹𝘁 𝗧𝗵𝗶𝘀
I wanted to create something both visually engaging and functionally robust to mimic the complexity of a real-world booking interface — while ensuring a great user experience through visual cues and persistence.
Code
import { useState } from 'react'
import './App.css'
const cinemaLayout = Array(7).fill().map(() => [
'W', 'W',
...Array(5).fill("A"),
'W', 'W',
...Array(5).fill("A"),
])
function App() {
const [matrix, setMatrix] = useState(JSON.parse(localStorage.getItem("matrix")) || cinemaLayout);
const handleSeatClick = (rowIdx, colIdx) => {
const newMatrix = matrix.map((row, rIdx) =>
row.map((seat, cIdx) => {
if (rIdx === rowIdx && cIdx === colIdx) {
if (seat === "A") return "S";
if (seat === "S") return "A";
}
return seat;
})
);
setMatrix(newMatrix);
};
const clearSelection = () => {
const newMatrix = matrix.map(row => row.map(seat => seat === "S" ? "A" : seat));
setMatrix(newMatrix);
};
const bookSeats = () => {
const newMatrix = matrix.map(row => row.map(seat => seat === "S" ? "B" : seat));
localStorage.setItem("matrix", JSON.stringify(newMatrix));
setMatrix(newMatrix);
};
return (
<div style={{display: "flex", flexDirection: "column", alignItems: "center"}}>
<h2>Book Tickets</h2>
<div className="seat-container">
{matrix.map((row, rowIdx) => (
<div key={rowIdx} className="seat-row">
<div className="row-label">{String.fromCharCode(65 + rowIdx)}</div>
{row.map((seat, colIdx) => (
<div key={colIdx}
className={`seat ${seat === "W" ? "walkway" : seat === "A" ?
"available" : seat === "S" ? "selected" : "booked"}`}
onClick={() =>
seat !== "W" && seat !== "B" ? handleSeatClick(rowIdx, colIdx) : null
}
>
{seat === "A" || seat === "S" || seat === "B" ? `${row.filter((s, idx) => idx < colIdx && (s === "A" || s === "S" || s === "B")).length + 1}` : ""}
</div>
))}
</div>
))}
</div>
<div className="controls">
<button onClick={bookSeats}>Book</button>
<button onClick={clearSelection}>Clear</button>
</div>
</div>
)
}
export default App
Thanks for reading
Subscribe to my newsletter
Read articles from krishna chaitanya directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
