Migrating from SPA to SSR, Part 1: The Decision

The breaking point that made me reconsider my single-page application architecture

I'll never forget the moment I realized something was wrong. It was a Tuesday morning, and I got three emails within an hour—all complaining about the same thing. "Your site takes forever to load." "Why does it show a blank screen?" "Is something broken?"

I checked the analytics. Average time to first contentful paint: 4.2 seconds. On mobile, it was worse—over 6 seconds. People were leaving before they even saw what my site was about. I refreshed the page myself, and I saw what they meant. A blank white screen. Then a loading spinner. Then finally, after what felt like forever, the content appeared. I'd been testing on my fast development machine with a wired connection. I hadn't really thought about what it felt like on a phone with spotty WiFi.

My React SPA had served me well for a while. It was fast once loaded, the developer experience was great, and I could build complex interactions easily. I'd spent months perfecting the animations, the state management, the component architecture. Everything worked beautifully—once it loaded. But I'd been ignoring the warning signs. The initial bundle was getting bigger with every feature I added. SEO was a constant struggle. I'd spend hours trying to get meta tags right, trying to make sure social media previews worked, trying to optimize for search engines that couldn't see my content. And now, real users were telling me what I'd been avoiding: the first impression was terrible.

The Breaking Point

The emails weren't the only problem. I'd been watching my search rankings slowly drop for months. I'd write a blog post, share it, and watch it get indexed. But it wouldn't rank. I couldn't figure out why. The content was good, the keywords were right, but Google just wasn't showing it to people. When I tested my site with their tools, I saw why—without JavaScript, there was almost nothing there. Just a loading spinner and empty divs. Google could see the HTML, but there was no content in it. The content was all loaded by JavaScript, and by the time that happened, Google had already moved on.

I tried all the usual fixes. Code splitting helped a bit. I broke my bundle into smaller chunks, loaded components lazily. It shaved off maybe half a second. Lazy loading images made a small difference. But I was fighting against the fundamental architecture. A single-page application loads everything upfront, and there's only so much you can optimize that. I could make the bundle smaller, but I couldn't make it load instantly. I could optimize images, but I couldn't make the initial HTML contain actual content.

The real wake-up call came when I tried to share a blog post on Twitter. I'd written something I was proud of, something I thought people would find useful. I hit share, and the preview card showed nothing—just a blank image. Twitter's crawler couldn't see my content because it wasn't server-rendered. It saw the HTML, but the HTML was empty. The content existed, but only after JavaScript ran. And Twitter's crawler doesn't run JavaScript. So to Twitter, and to anyone sharing my links, my site looked broken. That's when I knew I had to make a change.

I started researching what other people were doing. I read blog posts about performance, about SEO, about user experience. I joined forums, asked questions, watched talks. The consensus seemed clear: for content-heavy sites, server-side rendering was the way to go. But I'd invested so much in my SPA. Was I really going to throw all that away? I spent days going back and forth, trying to convince myself that the problems weren't that bad. But the evidence kept piling up. Users were complaining. Search rankings were dropping. Social shares looked broken. I couldn't ignore it anymore.

I also started paying attention to sites I admired. How did they handle loading? How did they handle SEO? I'd inspect their HTML, see how they structured things. Most of them were using server-side rendering. They had content in their HTML from the start. They loaded fast. They ranked well. I realized I was fighting against best practices, not just dealing with technical limitations.

The final straw was when I tried to show my site to a friend. They opened it on their phone, and I watched them wait. And wait. And wait. Finally, the content appeared, but by then they'd already started scrolling away. They weren't impressed. They didn't care about my beautiful animations or my clever state management. They just wanted to see the content. And I couldn't give them that quickly enough.

Exploring the Options

I started researching. Server-side rendering seemed like the answer, but I wasn't sure which framework to choose. Next.js kept coming up in conversations and articles. The more I looked into it, the more it made sense. It was built on React, which I already knew. The file-based routing felt familiar. The API routes meant I wouldn't need a separate backend. And the hybrid approach—static generation where possible, server rendering where needed—gave me flexibility I didn't have before. But I had concerns. Would I lose the interactivity I loved about React? Would the developer experience be worse? How much work would the migration actually be? I spent days reading documentation, watching tutorials, trying to understand what the migration would actually involve. The more I learned, the more I realized it wasn't as scary as I thought. I could keep using React. I could keep using the same components. I'd just be rendering them differently.

The Hesitation

I'll be honest—I almost talked myself out of it. The current setup worked, even if it wasn't perfect. Migrating felt like a huge undertaking. What if I broke everything? What if it wasn't actually better? I spent a week going back and forth. I'd wake up convinced I should migrate, then by afternoon I'd find reasons not to. The fear was real. I'd invested a lot of time in the current setup. I knew how it worked. I knew its quirks. Starting over felt risky. But then I realized I was letting fear make the decision for me. The site wasn't serving users well, and that was reason enough to try something new. So I made the call. I'd migrate to Next.js, use server-side rendering, and see if it actually solved the problems I was facing. I had no idea what I was getting into, but sometimes you have to jump and figure it out on the way down.


Note: This is a mock-up post created as part of the Feather blog template demonstration. The content is provided as an example to showcase the blog's features including markdown rendering, search functionality, tags, and more.

Feather is a blog template built for Next.js. You can use these example posts as a reference when creating your own content.