Help us improve
Share bugs, ideas, or general feedback.
From vercel
Make the Vercel Edge Network and CDN do what you want — cache layers and headers, ISR / PPR behavior on Vercel's runtime, on-demand revalidation, image optimization metering, bandwidth cost guardrails, and when to bypass to an external CDN (Cloudflare, Bunny). Use this skill when caching is misbehaving, when bandwidth or transform costs are spiking, when "why is this stale" or "why is this slow" questions land, or when a launch is imminent and the cache strategy needs review. Trigger on any "edge" or "CDN" question on Vercel.
npx claudepluginhub bpainter/composable-dxp-claude-marketplace --plugin vercelHow this skill is triggered — by the user, by Claude, or both
Slash command
/vercel:vercel-cdn-edgeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Owns the cache and edge-delivery layer. Pair with `vercel-fluid-compute` (cache reduces function invocations), `vercel-deploy-pipeline` (cache invalidation on deploy), `software-engineering-nextjs-cache` (the framework-level `"use cache"` decisions), `software-engineering-nextjs-performance` (image optimization patterns), and `contentful-delivery-optimization` (when CMS-driven cache shapes are ...
Guides technical evaluation of code review feedback: read fully, restate for understanding, verify against codebase, respond with reasoning or pushback before implementing.
Share bugs, ideas, or general feedback.
Owns the cache and edge-delivery layer. Pair with vercel-fluid-compute (cache reduces function invocations), vercel-deploy-pipeline (cache invalidation on deploy), software-engineering-nextjs-cache (the framework-level "use cache" decisions), software-engineering-nextjs-performance (image optimization patterns), and contentful-delivery-optimization (when CMS-driven cache shapes are involved).
sizes discipline saves money.Every deployment is fronted by Vercel's global edge network. Responses are cached at the edge based on:
Cache-Control, Next.js fetch options).Caching layers (top to bottom):
Browser cache
↓ (max-age, must-revalidate)
Vercel Edge cache (CDN)
↓ (s-maxage, stale-while-revalidate, surrogate keys for tag-based invalidation)
Vercel Function execution
↓
Origin (your code, your data sources)
Each layer has its own invalidation. Tag-based invalidation (revalidateTag) talks to the Edge cache layer; deploy invalidation flushes the Function-output cache.
Next.js 16 on Vercel has four routing-level cache shapes:
| Mode | When | Vercel behavior |
|---|---|---|
| Static (SSG) | generateStaticParams + no revalidate | Built at deploy, cached forever at edge until next deploy |
| ISR (Incremental Static Regeneration) | revalidate: N or revalidate: false with on-demand tags | Cached at edge; refreshed in background per tag or on time-based interval |
| PPR (Partial Prerendering) | experimental.ppr: true + Suspense for dynamic parts | Static shell + dynamic Suspense islands; Vercel streams the dynamic parts |
| Dynamic | cache: "no-store", dynamic: "force-dynamic", or runtime-only signals (headers(), cookies()) | Function executes per request; CDN doesn't cache |
For Slalom Composable DXP defaults: PPR or ISR with on-demand tag revalidation. Pure static for marketing pages with rare updates; dynamic only for personalized routes.
// In a server component fetch
const data = await fetch(url, {
next: {
revalidate: 3600, // safety-net TTL in seconds
tags: ["page:home", "hero:abc123"],
},
});
Then in a webhook handler or server action:
import { revalidateTag, revalidatePath } from "next/cache";
revalidateTag("hero:abc123"); // invalidates everything tagged with this
revalidatePath("/products/[slug]"); // invalidates this path's segments
Patterns:
hero:abc123) for surgical invalidation.articles, nav) for bulk invalidation.revalidatePath) when you don't know which entries changed but you know the affected page.For Contentful-driven sites, see contentful-webhooks and contentful-delivery-optimization for the canonical webhook → tag pattern.
ISR (time-based):
fetch(... { next: { revalidate: 3600 } })
Page is cached for up to 1 hour. After expiry, the next request triggers a
background regen; subsequent requests get the new version.
On-demand (tag-based):
fetch(... { next: { tags: ["page:home"] } })
+ webhook calls revalidateTag("page:home") on event
Page is invalidated the moment the event fires; next request rebuilds.
Combined (the right pattern):
fetch(... { next: { revalidate: 3600, tags: ["page:home"] } })
On-demand is primary; time-based is the safety net for missed webhooks.
For Slalom defaults: combined, with revalidate set high enough that the time-based branch rarely fires (3600–86400s depending on content cadence).
PPR splits a route into static + dynamic parts:
// app/page.tsx
export const experimental_ppr = true;
export default function Page() {
return (
<>
<StaticHero /> {/* prerendered */}
<Suspense fallback={<Skeleton />}>
<PersonalizedRecommendations /> {/* streamed in */}
</Suspense>
<StaticFooter /> {/* prerendered */}
</>
);
}
Vercel serves the static shell from the CDN immediately; the dynamic Suspense parts stream from a Function. Best of both worlds for marketing pages with lightly personalized elements.
When to use:
When to skip:
next/image on Vercel uses Vercel's image optimizer. Each unique combination of (source, width, format, quality) is a transform — billed per-transform.
Optimization rules:
images.remotePatterns in next.config.mjs. Vercel will refuse to proxy unknown hosts.sizes discipline. Tells the browser which width to request; Vercel only generates those.
<Image src={url} sizes="(min-width: 1024px) 50vw, 100vw" ... />
next/image picks AVIF / WebP automatically. Don't override unless you have a reason.priority on the LCP image only.For very heavy use (millions of unique images, complex art direction), consider:
Bandwidth (egress) is the cost line that surprises clients most. Drivers:
Cost control:
next/image with sizes properly.For very high bandwidth, the cost math sometimes favors an external CDN sitting in front:
User → Cloudflare → Vercel
↑
(Functions still run on Vercel)
Pros: Cloudflare's bandwidth pricing scales differently; gets cheaper at TB volumes. Cons: Two cache layers to manage; observability split; some Vercel features (Speed Insights) need careful configuration.
Decision threshold: if Vercel bandwidth costs exceed ~$2k/month, do the math on Cloudflare in front. Below that, native Vercel is operationally simpler.
Custom headers via vercel.json or next.config.mjs:
// next.config.mjs
async function headers() {
return [
{
source: "/(.*)",
headers: [
{ key: "Cache-Control", value: "public, max-age=0, s-maxage=3600, stale-while-revalidate=86400" },
{ key: "X-Frame-Options", value: "DENY" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
],
},
];
}
Cache headers behavior on Vercel:
s-maxage=N controls Vercel Edge cache TTL.max-age=N controls browser cache.stale-while-revalidate=N lets Vercel serve stale while revalidating in background.Cache-Control: no-store skips Vercel cache entirely.For Next.js app-router routes, prefer revalidate and tags over manual Cache-Control headers — Next.js manages the cache layer for you.
The first 24 hours after launch have predictable cliffs:
Mitigations:
generateStaticParams for known slugs so first request is a build-cache hit.revalidate for the first day, then loosen.cache: "force-cache" left on a personalized route. Different users see the same cached page. Audit before launch.revalidateTag("articles") on every article publish recomputes every page that lists articles. Use specific tags for high-traffic pages.sizes to constrain.cache: "no-store" path hammered by a polling client. Either cache or move to a streaming pattern.vercel.json rewrite shadowing Next.js routing. Two sources of truth disagreeing. Pick one.# Cache Strategy: [Project]
## Per-route mode
| Route | Mode | revalidate (s) | Tags | Notes |
|---|---|---|---|---|
| /(marketing) | PPR | 3600 | page:*, nav | static shell + personalized hero |
| /products/[slug] | ISR | 86400 | product:[id] | webhook-driven |
| /api/auth | dynamic | n/a | n/a | per-request |
## Invalidation triggers
- Contentful Entry.publish → revalidateTag("[contentType]:[id]")
- Algolia index sync → revalidateTag("search")
- Manual flush → POST /api/admin/flush
## Image strategy
- Source: Contentful / Vercel Blob
- Sizes default: "(min-width: 1024px) 50vw, 100vw"
- Quality: 75
- Format: auto
## Cost guardrails
- Bandwidth: spend cap $X
- Transforms: alert at Y% of monthly budget
- Function GB-s: alert at Y%
## Risks
- {launch-day cold cache plan}
- {high-traffic events}
vercel-fluid-compute.vercel-deploy-pipeline.software-engineering-nextjs-cache.software-engineering-nextjs-performance.vercel-observability.contentful-delivery-optimization, contentful-webhooks.../../references/vercel-foundations.md