netlify-frameworks

$npx mdskill add netlify/context-and-tools/netlify-frameworks

Route framework-specific deep work to the guides in this skill: `references/astro.md`, `references/nextjs.md`, `references/nuxt.md`, `references/sveltekit.md`, `references/tanstack.md`, `references/vite.md`.

SKILL.md

.github/skills/netlify-frameworksView on GitHub ↗
---
name: netlify-frameworks
description: Deploy and configure web frameworks on Netlify — build settings and SSR/edge adapters plus local platform emulation and env vars. Use when setting up or fixing a framework deploy (Next.js / Astro / Nuxt / SvelteKit / Remix / React Router / TanStack Start / SolidStart / Gatsby / Angular / Vite / Express / Hydrogen / Hugo / Eleventy / Vue / React), adding SSR or edge functions or middleware wired to Netlify context, fixing SPA redirect and catch-all rules, setting a build command or publish directory, or debugging "why isn't my env var updating" and framework build failures.
---

Route framework-specific deep work to the guides in this skill: `references/astro.md`, `references/nextjs.md`, `references/nuxt.md`, `references/sveltekit.md`, `references/tanstack.md`, `references/vite.md`.

## Env vars: modern rules (read first)

Env values are injected **at build time**. Any change (client- or server-side) requires a **redeploy** — editing a var in the UI/CLI does NOT reach the live site or already-deployed functions until a new build runs.

**Never use a client prefix for secrets.** Client-prefixed vars are inlined into the browser bundle:
`VITE_`, `NEXT_PUBLIC_`, `PUBLIC_`, `NUXT_PUBLIC_`, `REACT_APP_`, `GATSBY_`, `VUE_APP_`.

Client-embed prefixes by framework: CRA `REACT_APP_`, Gatsby `GATSBY_`, Next `NEXT_PUBLIC_`, Nuxt `NUXT_ENV_`, Vue CLI `VUE_APP_`.

**Scopes:** build-time access needs **Builds** scope; SSR/DSG runtime access needs **both Functions and Builds**. `netlify.toml` is read only during build — functions cannot read it at runtime; set runtime vars in UI/CLI/API.

Netlify build variables can't be used as values in the UI or `netlify.toml` env sections. Set them inline before the build command:
```toml
[build]
  command = "REACT_APP_CONTEXT=$CONTEXT npm run build"
```

## SPA redirects and the SSR catch-all footgun

SPAs (React, Vue CLI, Vite, Nuxt in SPA mode) need a rewrite to serve `index.html` for `pushState`:
```
/* /index.html 200
```

**Remove any SPA catch-all when adopting an SSR adapter.** A leftover `/* → /index.html 200` silently serves static `index.html` for SSR pages and API routes — user redirects beat adapter-generated routes.

## Local dev with platform emulation (no Netlify CLI)

Vite-based frameworks emulate Netlify primitives (functions, edge functions, blobs, Netlify Database, Cache API, Image CDN, redirects/rewrites, headers, env vars, AI Gateway) in the dev server:

| Framework | Plugin/module | Run |
|-----------|---------------|-----|
| Astro (5.12+) | built-in (Netlify Vite plugin auto-loaded) | `astro dev` |
| Nuxt | `@netlify/nuxt` | `nuxt dev` |
| React Router | `@netlify/vite-plugin` | `react-router dev` |
| SolidStart 2 | `@netlify/vite-plugin` | `vite dev` |
| TanStack Start | `@netlify/vite-plugin-tanstack-start` | (vite) |
| Vite | `@netlify/vite-plugin` | `npx vite` |

Still need `netlify dev` (Netlify CLI) for: Gatsby generated functions (run `netlify build` first), Angular SSR local test (`netlify serve`), and frameworks without a Vite plugin.

**`netlify dev` gotcha:** with both a custom `command` and a `targetPort` in `[dev]`, you must set `framework = "#custom"` — otherwise the detector runs and your custom command is silently ignored.

## Build settings by framework

| Framework | Build command | Publish |
|-----------|---------------|---------|
| Angular (standard) | `ng build --prod` | `dist/YOUR_PROJECT_NAME` |
| Astro | `astro build` | `dist` |
| Create React App | `react-scripts build` | `build` |
| Eleventy | `eleventy` | `_site` |
| Gatsby | `gatsby build` | `public` |
| Hugo | `hugo` | `public` |
| Hydrogen | `remix vite:build` | `dist/client` |
| Next.js (SSR/hybrid) | `next build` | `.next` |
| Next.js (static export) | `next build && next export` | `out` (`NETLIFY_NEXT_PLUGIN_SKIP=true`) |
| Nuxt 3 | `nuxt build` | `dist` |
| Nuxt 2 | `nuxt generate` | `dist` |
| React Router | `react-router build` | `build/client` |
| Remix (Vite) | `remix vite:build` | `build/client` |
| SolidStart 2 (Vite plugin) | `vite build` | `dist/client` |
| SolidStart 2 (Nitro) | `vite build` | `dist` |
| SolidStart 1.x | `vinxi build` | `dist` |
| SvelteKit | `vite build` | `build` |
| TanStack Start (1.132.0+) | `vite build` | `dist/client` |
| Vite | `vite build` | `dist` |
| Vue CLI | `vue-cli-service build` | `dist` |

Detection suggests these; override in `netlify.toml` or UI (project configuration > Build & deploy > Continuous deployment > Build settings).

## SSR / adapter setup

### Astro
`npx astro add netlify` installs the adapter and edits `astro.config.mjs`. Adapter needed for SSR and out-of-the-box Image CDN for `<Image />`. SSR → Netlify Functions; middleware → Edge Functions. Adapter-less deploy only if no server features and no Image CDN need. Skew protection from 5.15.0.

### Next.js (13.5+ only)
Zero-config via the OpenNext adapter (`@netlify/plugin-nextjs`). Do NOT pin the version — Netlify auto-updates each build. Treat the legacy adapter as read-only history, never a recommendation.
Adapter provisions: serverless function for SSR/ISR/PPR/route handlers/Server Actions; Edge Function for Middleware; Full Route + Data Cache; Image CDN with `next/image`.
Skew protection is opt-in: set `NETLIFY_NEXT_SKEW_PROTECTION=true`, redeploy. No automatic support for client `fetch` — direct calls with `x-deployment-id: process.env.NEXT_DEPLOYMENT_ID`. Details in `references/nextjs.md`.

### SvelteKit
```bash
npm install -D @sveltejs/adapter-netlify
```
```js
import adapter from '@sveltejs/adapter-netlify';
export default { kit: { adapter: adapter() } };
```
Replace `@sveltejs/adapter-auto` with the specific import. SSR routes → a `render` function.
- `split: true` → one function per route. **Incompatible with Edge Functions** (`edge: false` or omit).
- `edge: true` → SSR in a Deno edge function; can't combine with `split`.
- **Redirects NOT supported in `netlify.toml`** — use `_redirects`.
- Edge functions don't work locally with `netlify dev` for SvelteKit.

### React Router (7+)
New: `npx create-react-router@latest --template netlify/react-router-template`. Existing:
```bash
npm install @netlify/vite-plugin-react-router
```
Add `netlifyReactRouter()` to Vite plugins. Default target = Serverless Functions.
**Edge (Deno):** needs plugin v2.1.1+, set `edge: true`, and you **must** create `app/entry.server.tsx`:
```typescript
export { default } from 'virtual:netlify-server-entry'
```
Exclude your own function paths: `netlifyReactRouter({ edge: true, excludedPaths: ['/api/*'] })`.
**Moving back to Serverless:** remove `edge: true` AND delete `app/entry.server.tsx`.
Middleware (React Router v7.9.0+, plugin v2.0.0+): opt in via `future.v8_middleware`; import `netlifyRouterContext` from `@netlify/vite-plugin-react-router/serverless` (or `/edge` when `edge: true`); access `context.get(netlifyRouterContext)`.

### Remix
New: `npx create-remix@latest --template netlify/remix-template` (CLI prompts functions vs Edge Functions). Manual (Remix Vite required):
```bash
npm install --save-dev @netlify/remix-adapter
```
Add `netlifyPlugin()` from `@netlify/remix-adapter/plugin` to Vite plugins.

### Nuxt
SSR via Nitro, automatic on Nuxt 3. Local parity via `@netlify/nuxt` (`npx nuxi module add @netlify/nuxt`).
- SSR on Edge Functions requires a different Nitro deployment preset (not auto-detected).
- pnpm + Nuxt 3: set `PNPM_FLAGS=--shamefully-hoist`.
- `nuxt/image` auto-uses Netlify Image CDN; set remote domains in `nuxt.config.ts`.

### SolidStart
SolidStart 2 builds on Vite — **no SolidStart-specific adapter**. Install `@netlify/vite-plugin`:
```ts
import netlify from "@netlify/vite-plugin";
import { solidStart } from "@solidjs/start/config";
import { defineConfig } from "vite";
export default defineConfig({
  plugins: [solidStart(), netlify({ build: { enabled: true } })],
});
```
Publish `dist/client`. SSR routes, server functions, middleware → Netlify Functions, zero extra config.
**Nitro alternative:** add `nitro()`, use plain `netlify()` (no `build.enabled`), publish `dist`.
SolidStart 1: Nitro auto-configures; optionally set `preset: "netlify"` in `app.config.ts`; `vinxi build` / `dist`.

### TanStack Start
React (and Solid.js) full-stack; SSR/Server Routes/Server Functions/middleware → serverless functions.
```bash
npm install -D @netlify/vite-plugin-tanstack-start
```
Add `netlify()` to Vite plugins alongside `tanstackStart()`; `vite build` / `dist/client` (1.132.0+). Netlify CLI deploys require netlify-cli 17.31+. Older versions: see `references/tanstack.md`.

### Gatsby
- **5.12.0+ (adapter):** auto-detects and installs `gatsby-adapter-netlify` (zero-config). Generates functions `SSR`, `DSG`. No Essential Gatsby plugin needed.
- **5.11.0 or earlier (Essential Gatsby plugin):** auto-installs `@netlify/plugin-gatsby`; also manually install `gatsby-plugin-netlify` (required for SSR, Gatsby redirects, asset caching). Generates `__api`, `__ssr`, `__dsg`, `__ipx`. Skip via `NETLIFY_SKIP_GATSBY_FUNCTIONS` (all) / `NETLIFY_SKIP_API_FUNCTION` / `NETLIFY_SKIP_SSR_FUNCTION` / `NETLIFY_SKIP_DSG_FUNCTION`.
- Gatsby 5 requires Node 18.
- Large sites: set `GATSBY_EXCLUDE_DATASTORE_FROM_BUNDLE` to load datastore from CDN (avoids max function deploy size; slower first SSR/DSG load).
- Image CDN: set `NETLIFY_IMAGE_CDN=true` (Contentful/Drupal/WordPress source plugins). **Not supported on 5.12.x with adapter — upgrade to 5.13.0+.**
- `StaticImage` and `gatsby-transformer-sharp` don't work for SSR/DSG — host images on a CDN.

### Angular
SSR auto-configured via an Edge Function. Suggested dev: `ng serve` / `4200`.
- **SSR pages are NOT subject to `_redirects` or `netlify.toml` redirects** — SSR uses Edge Functions that run before redirects. Use Angular's built-in redirects.
- Access `Request`/`Context` in SSR via `netlify.request` / `netlify.context` providers (from `@netlify/edge-functions`); unavailable client-side or during prerendering. Test locally with `netlify serve`.
- `NgOptimizedImage` auto-uses Image CDN; set `remote_images` (array of regex) under `[images]` in `netlify.toml`.

### Express
Node 18.14.0+. Deploy as a Netlify Function via `serverless-http`:
```bash
npm i express serverless-http @netlify/functions @types/express
```
```ts
// netlify/functions/api.ts
import express, { Router } from "express";
import serverless from "serverless-http";
const api = express();
const router = Router();
router.get("/hello", (req, res) => res.send("Hello World!"));
api.use("/api/", router);
export const handler = serverless(api);
```
```toml
[functions]
  external_node_modules = ["express"]
  node_bundler = "esbuild"
[[redirects]]
  force = true
  from = "/api/*"
  status = 200
  to = "/.netlify/functions/api/:splat"
```
No frontend: set a placeholder build command (e.g. `echo Building Functions`). All Function limits apply; not recommended as background/scheduled functions.

### Hydrogen
Shopify stack on React Router 7. **SSR only on Netlify Edge Functions — Netlify Functions NOT officially supported.** Node 24+. Use the starter:
```bash
npm create @shopify/hydrogen@latest -- --template https://github.com/netlify/hydrogen-template
cp .env.example .env && npm run dev
```

## Static-site gotchas

### Hugo
Set `HUGO_VERSION` (any release after 0.19) in `[build.environment]` — a missing/mismatched version causes `exit code: 255`. Install themes as **git submodules** (`git submodule add ...`), not `git clone`.

### Eleventy
`eleventy` / `_site`. **Build plugins require editing `.gitignore`: change `node_modules` to `**/node_modules/**`** — otherwise Netlify plugins and Eleventy collide on `.netlify/plugins/node_modules/` and the build errors.

## Vite meta-framework support matrix
Astro (auto on 5.12+), Nuxt (via `@netlify/nuxt`), TanStack Start (via `@netlify/vite-plugin-tanstack-start`), React Router, SolidStart — all **full**. SvelteKit — **experimental**.

## Deploy via CLI (Express, Nuxt, React, Vite)
```sh
npm install netlify-cli -g
netlify init
```
Follow prompts to create/link the site and set build settings.

<!-- Node version floors (18.14.0+) are stated per-framework where documented; no cross-framework build-image default is given in sources. -->

<!-- system: agent-context/frameworks/system.md — human-owned, merged by ctx-gen; edit system.md, not this section -->
# Netlify house rules (frameworks)

These are org conventions, not docs facts — merged into the rendered skill by
ctx-gen and never generated. Owned by the skills maintainer.

1. Per-framework deep guides live in this skill: `references/astro.md`,
   `references/nextjs.md`, `references/nuxt.md`, `references/sveltekit.md`,
   `references/tanstack.md`, `references/vite.md` — route framework-specific
   work there before improvising.
2. Next.js: modern runtime (v5, Next ≥13.5) only — treat the legacy adapter
   as read-only history, never a recommendation.
3. Remove any SPA catch-all (`/* → /index.html 200`) when adopting an SSR
   adapter — user redirects beat adapter-generated routes, so a leftover
   catch-all silently serves static `index.html` for SSR pages and API routes.
4. Any env var change — client- or server-side — requires a redeploy. Values
   are injected at build time; editing one in the UI/CLI does not reach the
   live site or already-deployed functions until a new build runs.
5. `netlify dev` with both a custom `command` and a `targetPort` requires
   `framework = "#custom"` in the `[dev]` block — otherwise the detector runs
   and the custom command is silently ignored.
6. Never use a client prefix (`VITE_`, `NEXT_PUBLIC_`, `PUBLIC_`,
   `NUXT_PUBLIC_`, `REACT_APP_`, `GATSBY_`, `VUE_APP_`) for secrets —
   client-prefixed vars are inlined into the browser bundle.
7. Next.js skew protection is version-conditional: below Next 14.1.4 the
   `NETLIFY_NEXT_SKEW_PROTECTION` env var is not sufficient on its own —
   `experimental.useDeploymentId` (plus `useDeploymentIdServerActions` when
   server actions are used) must also go in `next.config.js`. Always ask for
   or state the version condition; never present the env var as the whole
   setup.
8. Client `fetch` calls are not covered by Next.js skew protection by default.
   Give both options. Next.js 15.4+ has an experimental `useSkewCookie` flag
   that carries the deployment identifier in a cookie so it rides along on
   client `fetch` calls; Netlify supports it, but say it is not
   production-ready and that it holds visitors on the older deploy until the
   cookie clears. The other option, on any version, is adding
   `x-deployment-id` with `process.env.NEXT_DEPLOYMENT_ID` per call.

More from netlify/context-and-tools

SkillDescription
netlify-access-controlPicks the right Netlify site-protection layer and disambiguates the three unrelated "auth" concepts users conflate — app-user login (Netlify Identity), site-load gating (Password Protection / project visibility), and dashboard SAML SSO. Use it when asked to password-protect a site or Deploy Preview, make a project private/public, restrict a site to your team, require SSO to view a site, set up company-wide or internal-app SSO, invite users to a private project, or know which team member made a request to a private site. Also use it for SSO-session symptoms on protected sites: "logged out mid-session", 401s after about an hour, or token expiry/refresh questions. Not for wiring auth code — route app-login setup to netlify-identity.
netlify-agent-runnerRun AI agent tasks remotely on Netlify using Claude, Codex, or Gemini. Use when the user wants to run an AI agent on their site, get a second opinion from another model, or delegate development tasks to run remotely against their repo.
netlify-ai-gatewayUse Netlify AI Gateway to call OpenAI, Anthropic Claude, Google Gemini, TypeSafe (Jev), or OpenRouter-hosted models (xAI/DeepSeek/Meta/Mistral/Qwen) from Netlify Functions or Edge Functions without managing provider accounts or API keys. Reach for this when adding an AI feature to a Netlify app — a chatbot, text summarizer, image generator, joke/content generator, form-submission routing or analysis, or any LLM call — or when wiring the OpenAI/Anthropic/Gemini/TypeSafe/OpenRouter SDK into a Netlify Function, choosing which env vars to use, streaming long generations, or debugging why gateway calls fail at build time or return 401.
netlify-blobsStore and retrieve unstructured objects, files, and cache-like state on Netlify with the @netlify/blobs module. Use when persisting user file uploads (images/documents), caching computed output from functions or Background Functions, serving downloadable assets, storing JSON blobs keyed by ID, or seeding deploy-specific data. Reach for this for key/value or object storage from Functions, Edge Functions, or Build Plugins — not for per-user, transactional, or relational data (use Netlify DB for that). Triggers include "save an uploaded file", "cache API results", "store generated site map", "key/value store for a function", or "file uploads without a database".
netlify-cachingCache dynamic and static responses on Netlify's CDN from Functions, Edge Functions, and proxies. Use when you add caching or cache-control headers to a function response, tune cache TTL or stale-while-revalidate, set up the durable cache, vary a cache key by query/header/cookie/country/language, purge or invalidate the cache by site or cache tag, use the programmatic Cache API (caches.open/match/put) or @netlify/cache helpers (fetchWithCache/cacheHeaders/getCacheStatus), speed up an expensive API call, add ISR or on-demand revalidation, or debug why a response is or isn't cached via the Cache-Status header.
netlify-configConfigure Netlify builds and routing via netlify.toml, _redirects, and _headers. Use when setting a build command or publish directory, adding redirects or rewrites or proxies, adding an SPA fallback rewrite, setting custom response headers or basic auth, managing environment variables and secrets, scoping vars per deploy context, marking a var as secret, disabling secret scanning, configuring functions bundling, ignoring builds, or wiring up a monorepo or JavaScript SPA on Netlify.
netlify-databaseZero-config Postgres for Netlify apps via @netlify/database — querying data from Functions/Edge Functions, writing schema migrations, setting up Drizzle ORM, local dev with netlify dev, database branches for deploy previews, and migrating an existing Postgres project onto Netlify. Use when adding a database, building a contact form or CRUD API, writing SQL migrations, wiring up Drizzle, running netlify database commands, testing with a local Postgres, or switching from Neon/Supabase/RDS to Netlify Database.
netlify-deployCreate, configure, and manage Netlify deploys from code — reach for this when setting up Git continuous deployment, running netlify deploy or netlify deploy --prod from the CLI, writing netlify.toml deploy contexts, adding a Deploy to Netlify button, wiring build hooks, configuring Deploy Previews or branch deploys, locking or skipping deploys, fixing a failed or secrets-scanning deploy, or when someone asks to "deploy my site", "set up preview deploys", "add per-branch build config", "keep staging out of Google", or "add a deploy button to my README".
netlify-edge-functionsWrite, route, cache, and debug Netlify Edge Functions, the Deno-based TypeScript or JavaScript handlers in netlify/edge-functions that run at the network edge in front of static files and serverless functions. Activate this skill whenever someone wants to add auth middleware or a login gate, redirect or rewrite visitors by country or cookie, localize or personalize a page with geolocation, run A/B testing, transform or inject HTML into a response, stream server-side rendering at the edge, or cache an edge response. Also use it when an edge function silently does nothing, runs in the wrong order, ignores its Cache-Control headers, or cannot read an environment variable.
netlify-formsServerless form handling on Netlify-hosted sites — detects HTML forms at deploy time, stores submissions, filters spam, and sends notifications. Use when adding a contact form, lead-capture form, file-upload form, or newsletter signup to a Netlify site; wiring AJAX form submission; setting up a custom thank-you page; adding a honeypot or reCAPTCHA to a form; getting forms working in Next.js, Nuxt, SvelteKit, Astro, or Gatsby; reading form submissions via the Netlify API; setting up submission notifications or ChatGPT automations on form submission events; or debugging missing submissions and forms that silently fail to register.