Vercel vs. AWS Amplify: The Bundled Backend Is the Real Question
13 runtime dependencies, zero AWS SDK packages, and a build that splits 392 static pages from 8 dynamic routes with no deployment config authored for either.
Vercel deploys a Next.js app and gets out of the way — the framework's own static/dynamic split becomes the deployment topology, with zero YAML. AWS Amplify deploys the same app onto AWS's broader stack, and offers to also be your auth provider, GraphQL API and database. Choose Vercel when the backend is already decided; choose Amplify when you want AWS's services bundled with the hosting. This storefront picked the first shape, on purpose, and ships zero AWS SDK packages as a result.
The comparison at a glance
| Vercel | AWS Amplify | |
|---|---|---|
| What it deploys | Next.js, framework-aware (SSG/ISR/SSR/Edge inferred per route) | Any front end, plus optional backend resources it provisions |
| Bundled backend | None — bring your own | Cognito (auth), AppSync (GraphQL), DynamoDB, S3 |
| Config surface | next.config.ts, mostly optional | amplify.yml build spec + backend resource definitions |
| Image optimization | Built-in on-demand resizing (a paid dimension, if used) | Not part of the platform — you wire your own |
| This repo's usage | Hosting + @vercel/analytics only | Not used at all — zero @aws-sdk/* packages |
| Where the backend actually lives | Supabase (Postgres, RLS, Auth, Storage) | Would be Amplify's own Cognito/AppSync/DynamoDB stack |
The decision this repo actually made
package.json has 13 runtime dependencies. None of them starts with @aws-sdk/, and none is aws-amplify:
"dependencies": {
"@mdx-js/loader": "^3.1.1",
"@mdx-js/react": "^3.1.1",
"@next/mdx": "^16.3.0",
"@supabase/ssr": "^0.12.0",
"@supabase/supabase-js": "^2.110.0",
"@vercel/analytics": "^2.0.1",
"lucide-react": "^1.21.0",
"next": "16.2.9",
"react": "19.2.4",
"react-dom": "19.2.4",
"rehype-slug": "^6.0.0",
"remark-gfm": "^4.0.1",
"server-only": "^0.0.1"
}
Two of those, @supabase/ssr and @supabase/supabase-js, are the entire backend: Postgres with row-level security, auth, and file storage for the gated downloads. That's the actual comparison Amplify invites — not "where does the HTML get served," which both platforms answer well, but "who provides auth, the database, and the API." Amplify's answer is Cognito + AppSync + DynamoDB, provisioned and configured through the platform itself. This repo's answer is a separate managed Postgres project reached over https, with RLS enforcing access at the row level rather than through IAM policies — the reasoning for that specific choice, and the real policy count behind it, is Supabase vs Firebase. Vercel has no opinion on that decision either way; it hosts whatever the app calls out to — though it will happily provision a Postgres for you through its own marketplace, which is the narrower comparison in Vercel Postgres vs. Supabase.
Where Vercel's framework-awareness actually shows up
The distinction isn't marketing — it's visible in what a build does with each route. A next build of this repository today compiles 392 pages to static HTML at build time and leaves exactly 8 routes dynamic, server-rendered per request:
ƒ /api/download
ƒ /api/webhooks/lemonsqueezy
ƒ /auth/callback
ƒ /dashboard
ƒ /dashboard/settings
ƒ /login
ƒ /reset-password
ƒ /signup
ƒ Proxy (Middleware)
Vercel reads that build output and deploys accordingly — the 392 static pages go straight to the CDN edge, and the 8 dynamic routes plus the proxy become functions, with no deployment config authored for any of it. Amplify's Next.js support works from the same build output, but arriving there means the platform's own build pipeline (amplify.yml) has to be told how to run next build and what to do with the result; it isn't the platform Next.js's own team ships the framework alongside. Neither gap is disqualifying — it's the cost of Amplify's real advantage, which is provisioning AWS resources (a Cognito pool, a DynamoDB table) from the same place that hosts the front end.
What Vercel charges for that this app doesn't use
next.config.ts has no images block. That's not an oversight — this catalog serves 111 products' screenshots as plain <img> tags pointed at pre-built WebP variants (cardImage() / viewImage() in src/lib/imageVariants.ts), generated once at build time by scripts/images/build-variants.ts rather than resized per-request by Vercel's image optimizer. Vercel's optimizer is billed per source image transformed; a catalog this size run through it live would be a real, recurring cost line. Amplify doesn't have an equivalent built-in image pipeline to compare against — it's simply not a dimension either platform charges this app for, because the app opted out of the one that would have.
The session layer is the other place Vercel-specific behavior matters. src/proxy.ts runs on every request to refresh the Supabase session and rewrite auth cookies before a Server Component renders:
// src/proxy.ts
export async function proxy(request: NextRequest) {
let response = NextResponse.next({ request });
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{ cookies: { /* read + rewrite request/response cookies */ } },
);
// …getClaims(), never getSession() — see auth-react
}
This is Next.js middleware, not a Vercel-specific API — it would run the same way on Amplify's Next.js compute, or anywhere else that executes the framework's own server runtime. The point isn't portability; it's that neither platform's marketing page is where this behavior lives. It's in the framework, and both platforms are graded on how transparently they run it.
Security headers are the framework's job either way
The same "who owns this config" question applies to response headers. This repo's CSP, HSTS and frame-blocking rules are declared once, in the framework's own headers() hook:
// next.config.ts
const nextConfig: NextConfig = {
async headers() {
return [{ source: "/(.*)", headers: securityHeaders }];
},
};
That single function is what emits Content-Security-Policy, Strict-Transport-Security, X-Frame-Options and X-Content-Type-Options on every response. Vercel reads it directly out of the Next.js build and applies it — no separate configuration surface. On Amplify, the equivalent typically means either the same next.config.ts hook (if running Amplify's SSR compute, which does execute the framework's own server) or a CloudFront response-headers policy layered on top if serving through Amplify's static hosting path instead. Neither is hard, but it's one more place the two platforms diverge on how much of AWS's broader toolkit a Next.js-specific concern ends up routed through.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
A route that should be static shows up dynamic in next build output | Something in it reads request-time data — cookies, headers, searchParams in a Server Component | Check for cookies()/headers() calls or an un-awaited dynamic API; see Next.js static rendering |
Deploying to Amplify and the build can't find next.config.ts output | Amplify's Next.js support needs its own build-spec awareness of the framework version | Confirm the amplify.yml uses Amplify's Next.js 13+ SSR support, not the legacy static-export path |
| Vercel image-optimization costs spike unexpectedly | Every unique next/image src+size combination is billed as a separate source transformation | Pre-build fixed-size variants at deploy time (this repo's approach) if the image set is finite and known ahead of request time |
getSession() returns a session that later 401s on Amplify's own auth | Amplify's Cognito tokens have a different validation path than an unrelated provider's | Irrelevant here since this app never adopted Cognito — but the general lesson holds on any platform: validate the token server-side, never trust a client-supplied session object |
| A team assumes Amplify is "free" for the backend once already using it for hosting | Cognito/AppSync/DynamoDB are separately metered AWS services, not bundled compute | Price the backend services independently of the hosting decision — the two are billed on unrelated axes |
Frequently asked questions
Is AWS Amplify cheaper than Vercel? It depends what's being compared. Amplify's hosting tier and Vercel's are broadly similar for a static/SSR front end. The cost difference shows up if the backend also runs on Amplify's bundled services (Cognito, AppSync, DynamoDB), which are billed as separate AWS resources either way — so the honest comparison is Vercel-plus-your-backend-of-choice against Amplify-plus-its-bundled-backend, not the hosting tiers alone.
Does Vercel lock you into Vercel-specific APIs? Very little of this repository does. The proxy is standard Next.js middleware, the analytics package is optional and swappable, and the rest of the app — including the entire backend — is Supabase, reachable from any host. The image optimizer is the one genuinely Vercel-specific service this app deliberately opted out of.
Can you run a Supabase backend on AWS Amplify hosting? Yes — nothing about Amplify's hosting requires using its bundled backend services. This repo's actual comparison isn't really "Vercel vs. Amplify" as hosting platforms; it's "a specialized Next.js host plus a chosen backend" vs. "a general AWS host with its own backend bundled in," and the bundling is Amplify's real differentiator, not the hosting itself.
Why does this matter for choosing a template's hosting? Because the backend decision usually costs more engineering time than the hosting decision. A landing page template with no backend runs identically well on either platform. The moment auth, a database, or file storage enters the picture, the real question is which backend model — RLS on Postgres, or IAM-scoped Cognito/DynamoDB — fits the data, not which platform's dashboard looks better.
Templates in this post
ASoc Surge, an AI startup landing page, and ASoc Synth, an AI workspace SaaS landing page, both ship as pure static builds — the case where the Vercel-vs-Amplify hosting question genuinely doesn't touch a backend decision at all. ASoc Tempo, a time-tracking SaaS landing page, is the closer case: a marketing shell for a product that needs auth and a database once it's wired up, which is exactly where the backend question this post covers stops being hypothetical.
Browse the full sets: Next.js landing page templates and Tailwind landing page templates.
