DEV Community

Cover image for Virtual Scrolling, Lazy Loading & a Little Bit of Crying: Optimizing My Movie App
Shravan Bobade
Shravan Bobade

Posted on

Virtual Scrolling, Lazy Loading & a Little Bit of Crying: Optimizing My Movie App

🎞️ Virtual Scrolling, Lazy Loading & a Little Bit of Crying: Optimizing My Movie App

Let’s be honest — I just wanted a smooth movie app.

No jank. No stutter. Just silky-smooth scrolling like Netflix on a Sunday morning.

What I got was a rabbit hole of virtualization, broken layouts, and spacing problems that haunted my dreams.

But hey… we made it (barely).


🪜 Step 1: Added Lazy Loading with Pagination

So TMDB API said:

“Bro, I give you 20 movies per page. That's it.”

Me:

“Cool, I’ll just keep asking you again and again.”

I added lazy loading logic:

  • Introduced page and hasMore state
  • Updated fetchMovies() to append new movies instead of replacing the list like Thanos snapping away my previous results
  • Hooked up the Intersection Observer API to keep an eye on the bottom of the list like a snitch. Whenever it saw the last card, boom — next page.

Honestly, lazy loading felt great. Not too tricky. It worked.

Confidence level: 100

Reality level: about to drop


💀 Step 2: Enter Virtual Scrolling (aka the Boss Level)

Scrolling was smooth… until the list got long. Real long. Like “my fan sounds like a jet engine” long.

So I said: “Let’s virtualize this bad boy.”

I found the magic duo:

  • react-window
  • react-virtualized-auto-sizer

👷‍♂️ Created a new component VirtualizedMovieGrid

🧠 Only renders the movies actually visible in the viewport.

📐 Dynamically calculates column count based on screen width

Everything was going great until…

💥 IntersectionObserver no longer worked because… the DOM wasn’t real anymore.

No actual DOM nodes = nothing to observe.

Switched to using onItemsRendered from react-window.

That saved the day. Kinda. Almost. Temporarily.


😵 Step 3: My Grid Betrayed Me

You know what they don’t tell you?

FixedSizeGrid only works if your cards are exactly the same height.

Which my cards… were not.

So my rows turned into this weird puzzle of overlapping nonsense. I felt like I was watching Inception — but in CSS.

Solution?

👉 Switched to VariableSizeGrid

👉 Dynamically calculated row heights

👉 Added caching because performance was dying

👉 Realized I had to position everything absolutely/relatively

👉 And then... responsiveness said BYE.


😭 Styling: My Personal Boss Fight

I spent more time tweaking margins than I spent watching actual movies this year.

  • Mobile was weird
  • Desktop was okay
  • Tablets? Who even owns those? (Still fixed them)
  • I cried a little.

Eventually, I added responsive spacing that:

  • Shrinks spacing on mobile to make cards bigger
  • Expands spacing on desktop for better layout
  • Respects the user's device, but NOT my sanity

It still isn’t perfect, but if you squint... it looks great.

(And if you don’t squint, just pretend it’s abstract art.)


🎉 Final Result: Scroll Nirvana Achieved

✅ Lazy loading with infinite scroll

✅ Virtual rendering with smooth UX

✅ Responsive, dynamic layout

✅ Survives even large datasets like TMDB's trending chaos

✅ My brain? Slightly melted, but satisfied


📌 Key Takeaways

  • React Window is magical… until styling gets involved
  • Fixed size is cute, but variable size is reality
  • Responsive layout + virtualization = many hours of trial and error
  • Don’t trust scrollbars
  • Tea/Coffee is a legitimate debugging tool

Image description


🔗 Try It Yourself

🧪 Live app: https://shravandev.com/movienest/

📖 Part 1: Building MovieNest: Design, Proxy, and Debounce


💬 P.S.

If you’ve ever battled virtualization + layout + responsiveness all in one go… I see you.

We are warriors of the grid.

Let me know how you handled it (or if you’re still stuck in the scroll trenches 😅).


Built with tears, love, and position: relative; by Shravan 💻
Follow my dev chaos on GitHub,Linkedin

Top comments (0)