Prayag Dave

Building
scalable 
solutions
BACK

Fixing SEO & Social Previews in SPAs Without SSR

5 min read

•

0 views

CRA (Client Rendered Application) and SPA (Single Page Application) are super popular for building modern web apps. But if you’ve worked with them, you’ve probably run into these annoying issues:
  • Bad SEO (Search engines struggle to index them properly)
  • No Social Previews (Links don’t generate those nice preview cards with images & descriptions)
These issues can be a dealbreaker, which is why many developers move to SSR frameworks like Next.js or Gatsby to fix them. But what if you actually want to stick with a SPA or CRA? Are you just out of luck?

Not really! Here’s the deal:

SEO Isn't as Bad as It Used to Be

Search engines have gotten better at handling JavaScript-heavy apps. They can now download, parse, and render them, so basic SEO isn’t as terrible as it was a few years ago. Is it as good as SSR? No. But it’s way better than nothing.

Social Previews Are Still a Pain

This is where things get messy. Even if you use something like react-helmet to insert Open Graph meta tags, social media crawlers don’t wait for JavaScript to load. They just scrape the raw HTML — meaning your links won’t have a proper title, description, or image. Instead of a nice preview card, you get… nothing.

So, is there a fix?

I spent way too much time looking for a solution. The only real way to fix this is to modify the HTML before it gets sent to the client. That means intercepting the request for index.html and dynamically injecting meta tags.

Enter Cloudflare Workers 🛠️

Think of Cloudflare Workers like a global, serverless edge function that sits between users and your site.
✨ Low latency
✨ Crazy fast deployments
✨ No expensive cloud bills
Read more about them here.

🚀 Injecting SEO Metadata with Cloudflare Workers

Cloudflare Workers are serverless functions that run on Cloudflare’s global network. Think AWS Lambda, but faster, cheaper, and way easier to deploy.

1: Create a Cloudflare Workers Project

First, install the CLI and create a new project:

2: Define Metadata for Each Route

Inside index.ts, I created a simple Record<K, V> object:

3: Intercept Requests & Inject Meta Tags

In the event listener, we:
  1. Extract the requested route
  1. Check if we have metadata for it
  1. Fetch the original HTML & modify it before serving
If for some reason the <head> tag is missing, we fall back to injecting it at the <html> level:

4: Deploy & Set up Routing

Once deployed, set up a worker route to intercept specific routes (e.g., /commercial/*) that need metadata:
Now, your social previews will work 🎉!
Why only specific routes? Because running the worker on /* (all routes) means way more function invocations, which costs money. Being selective helps keep things efficient.

🎯 Testing Your Social Previews

To verify that it’s working, use this free tool:
đź”— OpenGraph.xyz
Just paste in your URL, and if everything’s set up right, you should see a preview card with the correct metadata!

Bonus: What if I’m not using Cloudflare?

Good news—most hosting providers now have their own Edge Functions that can do the same thing:
  • Vercel → Vercel Edge Functions
  • Netlify → Netlify Edge Functions
  • AWS → Lambda@Edge
So if your site isn’t on Cloudflare, you can still apply the same concept using your provider’s edge computing features.

Wrapping Up

This issue bothered me for a long time, so when I finally figured it out, I had to share it. Hopefully, this helps if you're struggling with SEO & social previews on SPAs!
 
Thanks for reading & happy coding! 🚀
 
 
 
Â