Frontends are hard
Modern frontends like Next.js, Svelte, Remix, Astro, etc. are hard to deploy. There are services like Netlify and Vercel that have built custom infrastructure just to do this.
In this post we look at why frontends have become so hard to deploy and why even the giant cloud providers like AWS, GCP, or Azure have really poor support.
We also look at how some recent changes have made it possible for SST to better support them.
Background
Back in the 2010s frontends were single-page apps. Hosting them was really easy. You just uploaded it to an S3 bucket and shared the URL with your users. To get fancy, you could add a CDN.
You could do this on any cloud provider. Like AWS or GCP, and it was the simplest part of your stack.
Modern frontends now need a combination of infrastructure like S3 buckets, serverless functions, databases, CDNs, edge functions, edge data stores, and more.
There are also a dozen or so competing frameworks.
This is complicated enough that even AWS and GCP’s services like Amplify and Firebase have really poor support for them. They only support a couple of frameworks, don’t support their latest versions, and don’t cover all their features.
As a result, most people use secondary cloud providers like Netlify or Vercel. These services have their own custom infrastructure built on top of the major cloud providers. They’ve used this approach to grow to impressive scales. Both of these companies are reportedly doing around $100M in ARR.
But why do we need dedicated services? What’s so hard about deploying a frontend? How come even the giant cloud providers are unable to keep up?
Why is it so hard?
There are a few main reasons why frontends are hard to deploy and host:
-
Complicated infrastructure
With single-page apps, it’s all just static files. But modern frontends have evolved to support server-side rendering, API routes, image optimization, edge support, middleware, and a lot more. They are now closer to full-stack apps.
Modern frontends are now closer to full-stack apps.
This needs a combination of infrastructure like S3 buckets, serverless functions, databases, CDNs, edge functions, edge KV stores, etc.
There is no one-size-fits-all piece of infrastructure for hosting a modern frontend.
-
Dozen different frameworks
From Next.js to Astro, SvelteKit, Remix, SolidStart, to recent ones like TanStack Start or React Router v7 in Framework mode. There are at least a dozen competing frameworks with different features or areas of focus.
This leads AWS and GCP to pick which ones they support.
-
Faster pace of updates
All these frameworks are also constantly being updated.
The big cloud providers are slow to release updates on their end. Meaning that the versions they support are almost always out of date.
Caveats
You could self-host most of these frameworks in a container, or self-host as a single-page app. But these modes of deployment have their limitations and are clearly sub-optimal compared to using a dedicated service.
Open source for the win
When we had first started building SST, deploying frontends was a small part of what we did. But as we grew, we started to get more requests for better frontend support.
Self-hosting frontends is a perfect fit for an OSS project.
We looked at the above problems; not having a one-size-fits-all solution and a long tail of frameworks and features. And realized that this was a perfect fit for an OSS project.
So here’s what we did:
-
Break down the problem
Deploying a frontend is a combination of generating a build output through something called an adapter. And then using that to deploy the infrastructure.
While the infrastructure is specific to the provider, the adapter could potentially be shared.
So when we wanted to support frontends in SST, we decided to separate the two steps, and open source the adapters.
This is led us to start the OpenNext project.
Now providers like Cloudflare, and even Netlify and Vercel, can contribute to the project and help keep it up to date.
-
Open source everything
Aside from adapters, even the infrastructure SST uses to deploy frontends is open source. You can just look at our repo to see how we deploy them.
This allows people to contribute and makes it so that SST supports a large number of frameworks, covers all their features, and remains up to date with new releases.
-
Work closely with the framework authors
Since everything is open source, it also makes it easier for us to collaborate with the framework authors directly.
This made SST the best available option for people that want to deploy their frontend to their infrastructure.
Good or good enough?
The infrastructure that SST creates when you self-host your frontend is a little different from what Netlify or Vercel creates.
It stems from the fact that Netlify and Vercel are multi-tenant and can share some bits of infrastructure across all their users.
Whereas SST creates a completely isolated setup for your frontend where nothing is shared between your sites and you are of course isolated from other SST users.
The downside of our approach is that we recreate all the infrastructure, like the CDN distribution, for each of your sites. This means it can take 15-20 mins to create one of these. So if you have multiple frontends or if you were creating preview environments, your deployments will be slower.
And somewhat related, SST didn’t really support deploying your frontend to multiple regions.
We had been looking for ways to improve our setup. And now we can, thanks to this.

A new approach
A seemingly trivial pre:Invent announcement made CloudFront a lot more programmable. This paved the way for us to support setups that are far more flexible than Netlify or Vercel.
The key change here is that you can now use CloudFront Functions in tandem with a CloudFront KeyVaueStore to modify the origin of requests. This lets you create custom policies for how traffic is routed from the CDN to your application.
In the past, you’d have to use a Lambda@Edge function and a DynamoDB table which is both a lot more expensive and a lot slower.
Introducing Router
We used this change to build the Router
component. This component lets you use a single CloudFront distribution for your entire app.
const router = new sst.aws.Router("MyRouter", { domain: { name: "example.com", aliases: ["*.example.com"] }});
With it:
-
You can set up routes to your frontends.
sst.config.ts const web = new sst.aws.Nextjs("MyWeb", {router: {instance: router}});Or functions.
sst.config.ts const api = new sst.aws.Function("MyApi", {url: true,router: {instance: router,path: "/api"}});Or S3 buckets.
sst.config.ts const bucket = new sst.aws.Bucket("MyBucket", {access: "cloudfront"});router.routeBucket("/files", bucket);Or any URL.
sst.config.ts router.route("/external", "https://some-external-service.com"); -
You can configure it to serve a subdomain.
sst.config.ts new sst.aws.Nextjs("MyWeb", {router: {instance: router,domain: "docs.example.com"}});Or a path.
sst.config.ts new sst.aws.Nextjs("MyWeb", {router: {instance: router,path: "/docs"}});For this, you’ll also need to set the
basePath
in yournext.config.js
. -
You can share a router across stages. So your preview environments can deployed almost instantly.
sst.config.ts const router = $app.stage === "production"? new sst.aws.Router("MyRouter", {domain: {name: "example.com",aliases: ["*.example.com"]}}): sst.aws.Router.get("MyRouter", "A2WQRGCYGTFB7Z"); -
You can also deploy your frontends to multiple regions and it’ll route requests to the server function that’s nearest to your user.
sst.config.ts new sst.aws.Nextjs("MyWeb", {regions: ["us-east-1", "eu-west-1"],router: {instance: router}});
How it works
The Router uses a CloudFront KeyValueStore to store the routing data and a CloudFront Function to route the request. As routes are added, the store is updated.
So when a request comes in, it does a lookup in the store and dynamically sets the origin based on the routing data. For frontends, that have their server functions deployed to multiple regions, it routes to the closest region based on the user’s location.
Why this matters
Taking a step back for a second, a dedicated service like Netlify or Vercel is still likely to give you a better out-of-the-box experience.
Where SST shines, is when you start to grow. You start adding multiple frontends. You want serve your API from the same domain. Or you want your preview environments to include more than just your frontend.
This new setup allows you to do all of that. This is an example of one of SST’s design principles. You can start with the simplest setup, without using a Router. Then grow to adding it. And eventually customize it. All through code.
Learn more
You can learn more about this new setup:
Router
component docs- Guide on configuring a router
- The
router
prop in your frontend - Multiple
regions
in your frontend
Final thoughts
There are also some broader questions like:
- Why are there so many frameworks?
- Why do these frameworks change all the time?
- And do frontends need to be this complicated?
These are good questions that are worth having nuanced discussions about. We might tackle them in a future post. Or if you have some thoughts, we would love to hear them.