Migrating vercel.json to vercel.ts: What We Learned Moving to Typed Vercel Config
Table of Contents
About the Author

Ahmed Mahmoud
Author & Developer
Software engineer passionate about web development and user experience design.
Founder of Devya · eng-ahmed.com ↗Key takeaways
vercel.ts is the current recommended way to configure a Vercel project, replacing the static vercel.json file.
Importing VercelConfig from @vercel/config/v1 gives a typed config object, so a misspelled key like rewrtes fails the build instead of silently doing nothing — exactly what plain JSON can't catch.
Helper builders — routes.rewrite(), routes.redirect(), routes.cacheControl() — replace vercel.json's raw array-of-objects shape for rewrites, redirects, and headers.
vercel.ts is executable TypeScript evaluated at build/deploy time, so it can read environment variables and branch logic per environment, which a pure-JSON file structurally cannot do.
vercel.ts does not run per request. Request-time branching (A/B cookies, auth gates) still belongs in Next.js Middleware or Vercel's framework-agnostic Routing Middleware, not in this file.
At Devya we treat project config the way we treat anything that used to be a JSON file with opinions: as soon as it can be TypeScript, it should be. vercel.json never complained about a typo'd key, a malformed cron string, or a rewrite pattern that matched nothing — it just silently did nothing, and we found out in production. vercel.ts fixes that by making the config file a real module the TypeScript compiler checks before a deploy starts.
What is vercel.ts, and why does it replace vercel.json?
vercel.ts is a TypeScript file at a project's root that exports a typed config object describing build settings, routing, headers, and crons for a Vercel project. Vercel introduced it as the recommended configuration format, built on the @vercel/config package, to replace the schema-less JSON file every Vercel project has used since the platform's early versions.
The core problem it solves is that vercel.json is data, not code. A data file can't validate itself, can't reference an environment variable, and can't express "do X in preview, do Y in production" without a hand-rolled script generating the JSON before build. vercel.ts is a Node module, so all three become ordinary TypeScript.
What does a working vercel.ts look like?
A minimal but realistic one for a Next.js app proxying an API and running a nightly cleanup job imports routes and VercelConfig from @vercel/config/v1, then exports a config object setting buildCommand to the npm build script, framework to nextjs, a rewrite from /api/(.*) to a backend URL via routes.rewrite(), a permanent redirect from an old docs path via routes.redirect(), a long-lived cache header on static assets via routes.cacheControl(), and a nightly cron hitting a cleanup path on a standard crontab schedule.
Every field in that object is checked against the VercelConfig type at build time. Get a cron schedule string wrong, or pass a rewrite destination that isn't a string, and the build fails locally before a single byte reaches Vercel's build servers.
How do we migrate an existing vercel.json without breaking a working deploy?
Migrate one settings group at a time, not the whole file in one commit. First, install @vercel/config as a dev dependency. Second, create vercel.ts and port buildCommand and framework first, since those two fields are least likely to need logic. Third, port rewrites to routes.rewrite() calls, redirects to routes.redirect(), and headers to routes.cacheControl() or the generic header helper, matching each source and destination pair exactly. Fourth, port crons last, since a typo'd cron expression is the easiest mistake to miss in a diff review.
Run a full local build before deleting vercel.json, then ship to a preview deployment and diff the response headers and redirect behavior against the previous production deploy. Delete vercel.json only after the preview checks out. Keeping both files "just in case" means two sources of truth for the same settings, and that is a drift bug waiting to happen.
What actually breaks when config becomes executable TypeScript?
Three failure modes showed up in our own migrations, none of them hypothetical.
Config drifting between preview and production happens because vercel.ts runs as real code, so Date.now() or a random value baked into the config object differs from run to run. The fix is to treat vercel.ts as pure and deterministic: no clocks, no randomness, no network calls inside the file.
An environment variable reading as undefined inside the config happens because the file evaluates at build time, so it only sees variables exposed to the build step, not runtime-only secrets. The fix is confirming the variable is set for the Build environment in project settings, not only for Production or Preview runtime.
An old CLI ignoring the file entirely happens because a Vercel CLI version that predates vercel.ts support doesn't know to evaluate it. The fix is pinning the CLI version in CI and upgrading it deliberately, not as a side effect of a global install command.
Does vercel.ts replace next.config.ts too?
No. next.config.ts configures the Next.js framework itself — image domains, experimental flags, the Next-specific build pipeline. vercel.ts configures the Vercel platform layer underneath any framework — routing, crons, build command, and headers that apply regardless of what is generating the response. The two overlap at the edges, since both can define headers and redirects. Where they overlap, pick exactly one file to own a given rule; defining the same redirect in both is how a redirect loop ends up getting debugged at 2 a.m.
FAQ
Q: Does vercel.ts replace vercel.json entirely, or can we keep both?
A: Vercel still reads vercel.json if it is present, but running both for the same project invites the two files to disagree. Pick one and delete the other once the migration is verified.
Q: Does vercel.ts run on every incoming request?
A: No. It is evaluated at build and deploy time only. Per-request branching — auth gates, A/B cookie logic, geo-based routing — belongs in Next.js Middleware or Vercel's Routing Middleware, not in vercel.ts.
Q: Do we need to install anything extra to use vercel.ts?
A: Yes. Install the @vercel/config package and import VercelConfig and the routes helpers from @vercel/config/v1.
Q: Will an older Vercel CLI read a new vercel.ts file?
A: An older CLI that predates vercel.ts support will not evaluate it, so pin a current CLI version in CI before adopting the file.
Q: Does vercel.ts only work with Next.js?
A: No. It is framework-agnostic platform configuration, so it works the same way for Nuxt, SvelteKit, Astro, or a plain Node backend deployed on Vercel.
Further Reading
Frontend Engineering
Shipping Feature Flags in Next.js with the Vercel Flags SDK
We moved a client's ad-hoc environment-variable flags onto the Flags SDK and the precompute pattern. Here's what the migration looked like and why static rendering was the part worth protecting.
Frontend Engineering
Node.js 20 End-of-Life on Vercel: Our Migration to Node 24 LTS
Node.js 20 went end-of-life on Vercel on October 1, 2026. Here's what actually broke when our team migrated a handful of production projects to Node 24 LTS — and it wasn't application code.