Edge First React Apps with Vercel, Server Actions, and Turbopack


The web has entered a new era where the proximity of your server to your users is just as crucial as the quality of your frontend code. Gone are the days when deploying to a single region and serving everything from a monolithic backend sufficed. In 2025, speed is geography. React, long celebrated, has evolved to meet the edge native demand through new patterns and tooling. Paired with Vercel’s edge platform, Server Actions, and the still evolving Turbopack bundler, React applications can now deliver near instant interaction globally.
What Does “Edge First” Actually Mean?
“Edge first” reflects a fundamental shift in how we architect modern applications. Rather than delivering logic and content from a centralised location, edge first apps push both closer to the user. This reduces roundtrip latency and creates faster, more responsive experiences.
Take a user requesting product pricing from Tokyo when your server is in London. Traditional architecture would involve a transcontinental API call. Edge first architecture reroutes that request to the closest regional compute node, perhaps in Tokyo or Singapore, executing logic there before returning a response. React Server Components and Server Actions integrate directly with this model.
React’s Role: From UI Framework to Fullstack Runtime
With React 19 and the App Router in Next.js, components can now be split into Client and Server roles. This enables something previously awkward in React: executing logic on the server without creating an API layer.
Here’s a basic Server Action that updates a user’s profile:
// app/profile/actions.ts
'use server'
import { updateUserInDb } from '@/lib/db'
export async function updateProfileAction(formData: FormData) {
const name = formData.get('name') as string
const email = formData.get('email') as string
await updateUserInDb({ name, email })
}
You can then use this in a server rendered form like this:
// app/profile/page.tsx
import { updateProfileAction } from './actions'
export default function ProfileForm() {
return (
<form action={updateProfileAction}>
<input name="name" type="text" placeholder="Name" />
<input name="email" type="email" placeholder="Email" />
<button type="submit">Update</button>
</form>
)
}
No API controllers. No fetch calls. No state management libraries. Just pure React and built in form submission.
Why Vercel?
Vercel’s platform is tailor made for this architecture. Server Actions and Server Components are automatically deployed to the edge without special configuration. Functions run in the closest location to the user, without you needing to think about global deployment.
You also gain access to edge-specific features like geolocation, which can be injected into your components:
// middleware.ts
import { NextRequest, NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
const country = request.geo?.country || 'GB'
request.headers.set('x-country', country)
return NextResponse.next()
}
That header can then be read in Server Actions or Server Components to personalise logic or pricing.
Enter Turbopack: Replacing Webpack at Warp Speed
If Webpack defined the last decade of frontend development, Turbopack is defining the next. Written in Rust and designed for incremental builds, Turbopack enables edge first dev workflows that don’t slow you down.
Imagine you’re tweaking product card components across a large app with 100s of routes. With Webpack, a rebuild might take 3–4 seconds. With Turbopack, it’s often under 200ms.
You don’t even need to configure anything. If you’re using the Next.js App Router in 2025, Turbopack is already active.
In development, it's lightning fast:
npx vercel dev
Your Server Actions, Server Components, and streaming logic all execute in an emulated edge environment, so what you build locally behaves exactly like prod.
Example: Personalised Pricing at the Edge
Let’s say you’re running a global e-commerce app, and users need to see region specific pricing.
First, a Server Component fetches the base product and applies any modifiers:
// app/products/[id]/page.tsx
import { getProduct } from '@/lib/data'
import { getUserRegion } from '@/lib/geo'
export default async function ProductPage({ params }) {
const product = await getProduct(params.id)
const region = await getUserRegion()
const price = applyRegionalPricing(product.price, region)
return (
<div>
<h1>{product.name}</h1>
<p>Price: £{price}</p>
</div>
)
}
Here, getUserRegion()
might use the request header x-country
from middleware, and pricing logic executes directly on the edge where the request was received.
Now, suppose you want users to submit reviews. You can capture that via a Server Action:
'use server'
import { saveReview } from '@/lib/db'
export async function submitReview(formData: FormData) {
const productId = formData.get('productId') as string
const content = formData.get('review') as string
const rating = parseInt(formData.get('rating') as string)
await saveReview({ productId, content, rating })
}
Use this in a streamed component with React Suspense:
<form action={submitReview}>
<textarea name="review" required></textarea>
<input type="number" name="rating" min="1" max="5" />
<input type="hidden" name="productId" value={product.id} />
<button type="submit">Submit</button>
</form>
Because this executes at the edge, review submission is fast and snappy regardless of the user’s location.
Development Workflow
You’ll develop using Vercel’s local dev server which mimics the edge environment. You can still use next dev
, but vercel dev
is preferable for testing how server logic will behave when deployed globally.
React’s built-in useFormState
or useOptimistic
can improve the UX for forms interacting with Server Actions:
'use client'
import { useFormState } from 'react-dom'
import { submitReview } from './actions'
const initialState = { message: '' }
export function ReviewForm() {
const [state, formAction] = useFormState(submitReview, initialState)
return (
<form action={formAction}>
<textarea name="review" />
<input name="rating" type="number" />
<button>Send</button>
<p>{state.message}</p>
</form>
)
}
Caveats and Architecture Considerations
Edge compute isn’t always a silver bullet. If your database is in Europe and you’re running logic in Australia, you could end up incurring latency unless you adopt distributed data platforms like Turso, PlanetScale, or Neon.
Authentication must also be handled carefully. Session cookies, headers, and encryption all require consistency across regions. If you’re using third party auth like Clerk or Auth.js, make sure their edge support is enabled.
Avoid bloated server bundles. Since your Server Components and Actions are deployed to every edge region, importing large packages (like PDF rendering libraries or Stripe SDKs can inflate cold starts. Use dynamic imports wisely:
const stripe = process.env.NODE_ENV === 'development'
? await import('stripe-mock')
: await import('stripe')
Edge first isn’t just hype, it’s a necessity for delivering modern user experiences. With React’s new server primitives, Vercel’s edge platform, and the rocket speed of Turbopack, the tooling has finally caught up with the ambition. You're no longer constrained to centralised APIs or stale CDNs. You can run real logic at the edge. You can execute Server Actions in milliseconds. You can split your components by execution target and serve the lightest possible client. Building for the edge doesn’t require changing everything, but it does require thinking differently.
Subscribe to my newsletter
Read articles from Patrick Kearns directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
