webdev.complete
📡 SEO & Deployment
🚦Going to Production
Lesson 109 of 117
25 min

Deploy to Vercel

Connect repo, env vars, previews, prod, rollback, analytics.

There's a long version of how to deploy a web app - provisioning servers, setting up Nginx, configuring SSL, writing systemd units, debugging port 80 at midnight - and there's a short version, which is "push to GitHub and Vercel does the rest." This lesson is about the short version: how Vercel works under the hood, the workflow that makes it pleasant, and the handful of features (preview deploys, env vars, instant rollback, Skew Protection) that you should actually understand before you ship.

The one-time setup

You sign in to vercel.comwith your GitHub account, click "New Project", pick a repo, click deploy. Done. Vercel inspects the repo, detects your framework (Next.js, Astro, SvelteKit, Vite + React, "just static HTML", whatever), and configures itself.

From the CLI, the same setup is two commands:

bash
npm i -g vercel
vercel login
cd my-project
vercel              # creates the project, deploys a preview
vercel --prod       # promotes to production

Preview deploys: every PR is a live URL

This is the killer feature. Every push to every branch (and every commit on every open PR) builds a deployment at its own unique URL:

text
myapp.vercel.app                                   # production
myapp-git-fix-login-acme.vercel.app                # branch alias
myapp-7a3f9k2p-acme.vercel.app                     # immutable per-commit URL

The per-commit URL never changes once built - it's essentially a snapshot of the app at that exact commit. You can link to it in a PR and reviewers can click through a real, deployed version of the change without pulling the branch locally.

Stakeholder review is now a link
Designers, PMs, customers - anyone with the link can poke the feature on real infrastructure before merge. This collapses entire review cycles. If you take one habit from Vercel: link the preview URL in every PR description.

Environment variables per environment

Vercel has three environment scopes: development, preview, and production. You set vars for each in the dashboard or via CLI.

bash
# Add a var to all environments
vercel env add DATABASE_URL

# Add only to production
vercel env add STRIPE_SECRET_KEY production

# Pull all vars for the linked project into .env.local
vercel env pull

vercel env pullis the "onboard a new dev" command. They clone the repo, run that, and the local .env.local matches the team's configured vars. No more "hey what's the value of X" on Slack.

Use preview vars for preview-only secrets
Connect previews to a separate database, Stripe test mode, and any other side-effecting service. The cost of a bug toasting your production DB from a preview deploy is high and easy to avoid.

Instant rollback

Because every deployment is its own immutable build, "rollback" is just "promote an older deployment to be the production alias." It's instant - no rebuild, no DNS change. The Deployments tab in the dashboard has a "Promote to Production" button. Or:

bash
vercel rollback                          # interactive: pick a previous deployment
vercel promote myapp-7a3f9k2p-acme.vercel.app   # promote a specific URL

When something breaks in prod, the recovery is one click, takes a second, and reverts to the exact build that was working five minutes ago.

Skew Protection

Here's a subtle bug that's been ruining deploys for years. You ship a new version. Existing users have the old JavaScript loaded in their browser. They click a button, which fetches /_next/static/chunks/abc123.js - a chunk that used to exist but the new build deleted. They get a blank page or a broken feature. This is called deployment skew.

Vercel's Skew Protectionfixes it by routing an existing client's requests back to the deployment they originally loaded, for a grace period (usually 7 days, configurable). The user's old JS continues working until they refresh.

next.config.ts
import type { NextConfig } from "next";

const config: NextConfig = {
  experimental: {
    // Forwards a deployment-id header on fetches so Skew Protection can route correctly
  },
  // Skew Protection is configured per-project in the Vercel dashboard:
  // Settings → General → Skew Protection
};

export default config;
It's mostly automatic
For Next.js apps, Skew Protection is a single toggle in the Vercel dashboard. Other frameworks need a small library to forward the deployment ID on client-side fetches. Worth the 10 minutes for any app that holds a session across deploys.

Speed Insights and Web Analytics

Two opt-in features that Just Work for both first-party RUM:

  • Speed Insights - collects real-user Core Web Vitals (LCP, INP, CLS, FCP, TTFB) per route. Shows you 75th percentile, segmented by device and connection type. This is your field-data dashboard.
  • Web Analytics - cookieless page-view analytics. Lightweight (no GDPR banner needed in most jurisdictions), server-side aggregation.
app/layout.tsx
import { SpeedInsights } from "@vercel/speed-insights/next";
import { Analytics } from "@vercel/analytics/next";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        {children}
        <SpeedInsights />
        <Analytics />
      </body>
    </html>
  );
}

Log drains

Production logs scroll fast and Vercel only keeps a recent window in the dashboard. For real observability, configure a log drain: a destination (Datadog, Axiom, Logtail, your own HTTP endpoint) that receives every request and runtime log via webhook in real time. You keep them as long as you want, in the tool you already use.

Configured in the dashboard at Team Settings → Log Drains. Pick a provider, paste a URL or API token, done. Most providers (Axiom, Datadog, BetterStack) have one-click integrations.

Vercel CLI essentials

bash
vercel                       # deploy a preview from the current dir
vercel --prod                # deploy to production
vercel logs <deployment-url> # tail logs from a specific deployment
vercel env pull              # sync env vars into .env.local
vercel link                  # connect a local repo to an existing Vercel project
vercel inspect <url>         # show build info, source files, routing for a deployment
vercel domains add ex.com    # attach a custom domain

A typical workflow in 6 steps

  1. git checkout -b fix-login - branch off main.
  2. Commit and push. Vercel builds a preview, comments the URL on the PR.
  3. Reviewers click the preview link, poke the change in a real browser.
  4. Approve and merge to main. Vercel builds and deploys to production.
  5. Watch Speed Insights for any regressions in real-user vitals.
  6. If anything looks off, "Rollback" in the dashboard. Done in seconds.
Two phrases worth memorizing
"Did you check the preview deploy?" (instead of pulling and running locally). "Roll it back, we'll figure it out after." (instead of debugging in production while users hit 500s).

Quiz

Quiz1 / 4

What's a preview deployment?

Recap

  • Connect a Git repo to Vercel; pushes trigger builds. Production is the main branch.
  • Every push gets a preview URL. Share it in PRs; stop pulling branches to review.
  • Env vars are scoped development / preview / production.vercel env pull syncs them locally.
  • Instant rollback: every deployment is immutable; promote any past one in one click.
  • Skew Protection keeps old clients working during a deploy.
  • Add SpeedInsights and Analytics for first-party RUM and page views. Configure a log drain for long-term observability.
Built with Next.js, Tailwind & Sandpack.
Learn. Build. Ship.