---
title: "How to deploy Next.js, Astro, Nuxt, or SvelteKit to Netlify | Netlify Knowledge Base"
description: "Guides and articles to help you get the most out of the Netlify platform."
source: "https://www.netlify.com/knowledge-base/how-to-deploy-nextjs-astro-nuxt-or-sveltekit-to-netlify/"
last_updated: "2026-09-21T19:10:21.000Z"
---
## Key takeaways

Netlify detects most frameworks and suggests the right build command and publish directory. For server rendering you add that framework’s adapter, and for several frameworks that’s a single command: `npx astro add netlify` for Astro, `@netlify/plugin-nextjs` automatically for Next.js 13.5+. Two rules cause most of the trouble: environment values are injected at build time so any change needs a **redeploy**, and a leftover SPA catch-all redirect will silently break your SSR routes.

-   **Remove `/* /index.html 200` when you adopt an SSR adapter.** User redirects beat adapter-generated routes, so the catch-all serves static HTML for your server routes and API endpoints.
-   **Any env var change requires a redeploy.** Editing one in the UI doesn’t reach the live site or already-deployed functions.
-   **Never use a client prefix for secrets.** `VITE_`, `NEXT_PUBLIC_`, `PUBLIC_`, `NUXT_PUBLIC_`, `REACT_APP_`, `GATSBY_`, `VUE_APP_` all get inlined into the browser bundle.
-   **SSR runtime access needs both Functions and Builds scopes**, not just Builds.
-   **Vite-based frameworks emulate Netlify locally** without the CLI: functions, blobs, database, Image CDN, redirects, headers, AI Gateway.
-   **Don’t pin `@netlify/plugin-nextjs`.** Netlify auto-updates it each build.

## The framework part should be boring

Framework deploys go one of two ways. Either detection picks up your project, the suggested build settings are right, and it works on the first push. Or something subtle is off, and you spend an afternoon on a page that renders correctly locally and serves stale static HTML in production.

The second case usually has one of a small number of causes, and they’re the same causes across frameworks. This covers the build settings table you probably came for, the adapter setup per framework, and then the failure modes worth knowing before you hit them.

## Environment variables: read this first

Values are injected **at build time**. Any change, client-side or server-side, requires a **redeploy**. Editing a variable in the UI or CLI does not reach the live site or already-deployed functions until a new build runs. This is the single most common “I already fixed that” moment.

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

The client-embed prefix varies by framework: Create React App uses `REACT_APP_`, Gatsby `GATSBY_`, Next `NEXT_PUBLIC_`, Nuxt `NUXT_PUBLIC_`, Vue CLI `VUE_APP_`.

**Scopes.** Build-time access needs the **Builds** scope. SSR and DSG runtime access needs **both Functions and Builds**. And `netlify.toml` is read only during the build, so functions can’t read variables from it at runtime; set those through the UI, CLI, or API.

Netlify’s own build variables can’t be used as values in the UI or in `netlify.toml` env sections. Set them inline before the build command instead:

```
[build]  command = "REACT_APP_CONTEXT=$CONTEXT npm run build"
```

## The SPA catch-all footgun

Single-page apps (React, Vue CLI, Vite, Nuxt in SPA mode) need a rewrite so `pushState` routing doesn’t 404:

```
/* /index.html 200
```

**Remove it when you adopt an SSR adapter.** User redirects take precedence over adapter-generated routes, so a leftover catch-all quietly serves static `index.html` for SSR pages and API routes. Nothing errors. The page just isn’t server-rendered any more, and your API routes return HTML.

## Build settings by framework

Detection suggests these. Override in `netlify.toml` or the UI under project configuration > Build & deploy > Continuous deployment > Build settings.

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`

## Adapter setup by framework

### Astro

```
npx astro add netlify
```

That installs the adapter and edits `astro.config.mjs`. You need it for SSR and for out-of-the-box Image CDN support with `<Image />`. SSR routes become Netlify Functions; middleware becomes an Edge Function. You can deploy without the adapter only if you have no server features and no Image CDN need. Skew protection is available from Astro 5.15.0.

### Next.js (13.5+ only)

Zero-config through the OpenNext adapter, `@netlify/plugin-nextjs`. **Do not pin the version.** Netlify auto-updates it each build.

The adapter provisions a serverless function for SSR, ISR, PPR, route handlers, and Server Actions; an Edge Function for Middleware; the Full Route and Data Cache; and Image CDN for `next/image`.

Skew protection is opt-in and **version-conditional**. Set `NETLIFY_NEXT_SKEW_PROTECTION=true` and redeploy. On Next.js **earlier than 14.1.4** the env var is not sufficient on its own; add the deployment-id flags:

```
/** @type {import('next').NextConfig} */const nextConfig = {  experimental: {    useDeploymentId: true,    // only needed when using Server Actions    useDeploymentIdServerActions: true,  },};
```

There’s no automatic support for client-side `fetch`. Make those calls directly with `x-deployment-id: process.env.NEXT_DEPLOYMENT_ID`.

If you find guidance referencing the legacy Next adapter, treat it as history rather than a recommendation.

### SvelteKit

```
npm install -D @sveltejs/adapter-netlify
```

```
import adapter from '@sveltejs/adapter-netlify';export default { kit: { adapter: adapter() } };
```

Replace `@sveltejs/adapter-auto` with the specific import. SSR routes become a `render` function. Four things to know:

-   `split: true` gives one function per route, and is **incompatible with Edge Functions** (set `edge: false` or omit it).
-   `edge: true` runs SSR in a Deno edge function, and can’t be combined with `split`.
-   **Redirects are not supported in `netlify.toml`** for SvelteKit. Use `_redirects`.
-   Edge functions don’t work locally with `netlify dev` for SvelteKit.

### React Router (7+)

New project:

```
npx create-react-router@latest --template netlify/react-router-template
```

Existing project: `npm install @netlify/vite-plugin-react-router`, then add `netlifyReactRouter()` to your Vite plugins. The default target is Serverless Functions.

For **Edge (Deno)** you need plugin v2.1.1+, `edge: true`, and you **must** create `app/entry.server.tsx`:

```
export { default } from 'virtual:netlify-server-entry'
```

Exclude your own function paths with `netlifyReactRouter({ edge: true, excludedPaths: ['/api/*'] })`. Moving back to Serverless means removing `edge: true` **and** deleting `app/entry.server.tsx`.

Middleware needs React Router v7.9.0+ and plugin v2.0.0+. Opt in via `future.v8_middleware`, import `netlifyRouterContext` from `@netlify/vite-plugin-react-router/serverless` (or `/edge` when `edge: true`), and read it with `context.get(netlifyRouterContext)`.

### Remix

New: `npx create-remix@latest --template netlify/remix-template`, which prompts for functions vs Edge Functions. Manual setup requires Remix Vite:

```
npm install --save-dev @netlify/remix-adapter
```

Add `netlifyPlugin()` from `@netlify/remix-adapter/plugin` to your Vite plugins.

### Nuxt

SSR runs through Nitro, automatic on Nuxt 3. For local parity install `@netlify/nuxt` with `npx nuxi module add @netlify/nuxt`.

-   SSR on Edge Functions needs a different Nitro deployment preset, and it isn’t auto-detected.
-   On pnpm with Nuxt 3, set `PNPM_FLAGS=--shamefully-hoist`.
-   `nuxt/image` uses Netlify Image CDN automatically. Set remote domains in `nuxt.config.ts`.

### SolidStart

SolidStart 2 builds on Vite and has **no SolidStart-specific adapter**. Install `@netlify/vite-plugin`:

```
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, and middleware all become Netlify Functions with no extra config. For the Nitro alternative, add `nitro()`, use plain `netlify()` without `build.enabled`, and publish `dist`. SolidStart 1 auto-configures Nitro; optionally set `preset: "netlify"` in `app.config.ts` and use `vinxi build` with `dist`.

### TanStack Start

React and Solid.js full-stack. SSR, Server Routes, Server Functions, and middleware all become serverless functions.

```
npm install -D @netlify/vite-plugin-tanstack-start
```

Add `netlify()` to your Vite plugins alongside `tanstackStart()`, then `vite build` with `dist/client` on 1.132.0+. CLI deploys need netlify-cli 17.31 or newer.

### Gatsby

Which path you’re on depends on your version.

-   **5.12.0+ (adapter):** auto-detects and installs `gatsby-adapter-netlify`, zero-config. Generates `SSR` and `DSG` functions. No Essential Gatsby plugin needed.
-   **5.11.0 or earlier (Essential Gatsby plugin):** auto-installs `@netlify/plugin-gatsby`, and you also need to manually install `gatsby-plugin-netlify` for SSR, Gatsby redirects, and asset caching. Generates `__api`, `__ssr`, `__dsg`, `__ipx`. Skip them with `NETLIFY_SKIP_GATSBY_FUNCTIONS` for all, or the per-function variants.

Gatsby 5 requires Node 18. On large sites, set `GATSBY_EXCLUDE_DATASTORE_FROM_BUNDLE` to load the datastore from the CDN, which avoids the max function deploy size at the cost of a slower first SSR/DSG load. For Image CDN set `NETLIFY_IMAGE_CDN=true` (Contentful, Drupal, WordPress source plugins), though it’s **not supported on 5.12.x with the adapter**, so upgrade to 5.13.0+. And note `StaticImage` and `gatsby-transformer-sharp` don’t work for SSR or DSG; host those images on a CDN.

### Angular

SSR is auto-configured through an Edge Function. Suggested dev setup is `ng serve` on port 4200.

The important consequence: **SSR pages are not subject to `_redirects` or `netlify.toml` redirects**, because SSR runs in Edge Functions that execute before redirects. Use Angular’s built-in redirects instead.

Access `Request` and `Context` in SSR via the `netlify.request` and `netlify.context` providers from `@netlify/edge-functions`. They’re unavailable client-side and during prerendering. Test locally with `netlify serve`. `NgOptimizedImage` uses Image CDN automatically; set `remote_images` as an array of regex under `[images]` in `netlify.toml`.

### Express

Node 18.14.0+. Deploy as a Netlify Function via `serverless-http`:

```
npm i express serverless-http @netlify/functions @types/express
```

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); // v1 Functions API — serverless-http requires this pattern
```

```
[functions]  external_node_modules = ["express"]  node_bundler = "esbuild"[[redirects]]  force = true  from = "/api/*"  status = 200  to = "/.netlify/functions/api/:splat"
```

With no frontend, set a placeholder build command like `echo Building Functions`. All Function limits apply, and this isn’t recommended for background or scheduled functions.

### Hydrogen

Shopify’s stack on React Router 7. **SSR runs only on Netlify Edge Functions. Netlify Functions are not officially supported.** Node 24+. Use the starter:

```
npm create @shopify/hydrogen@latest -- --template https://github.com/netlify/hydrogen-templatecp .env.example .env && npm run dev
```

## Local development with platform emulation

Vite-based frameworks emulate Netlify primitives right in the dev server, with no Netlify CLI: functions, edge functions, blobs, Netlify Database, the Cache API, Image CDN, redirects and rewrites, headers, env vars, and AI Gateway.

Framework

Plugin or 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`

You still need `netlify dev` for Gatsby generated functions (run `netlify build` first), Angular SSR local testing (`netlify serve`), and any framework without a Vite plugin.

Support level: Astro, Nuxt, TanStack Start, React Router, and SolidStart are **full**. SvelteKit is **experimental**.

## Common failure modes

**Your SSR pages serve stale static HTML.** A leftover SPA catch-all. Remove `/* /index.html 200` when you adopt an adapter.

**You changed an env var and nothing happened.** Values are baked in at build time. Redeploy.

**Your SSR route can’t read an env var that works at build time.** Runtime access needs both the Functions and Builds scopes, not Builds alone.

**Your custom `netlify dev` command is being ignored.** If `[dev]` has both a `command` and a `targetPort`, you must set `framework = "#custom"`. Otherwise the detector runs and your command is silently dropped.

**Your Angular redirects don’t fire.** SSR pages bypass `_redirects` and `netlify.toml` because Edge Functions run first. Use Angular’s own redirects.

**Your SvelteKit redirects don’t fire.** SvelteKit doesn’t support redirects in `netlify.toml`. Use `_redirects`.

**Your SvelteKit build fails with `split` and `edge` together.** They’re mutually exclusive. Pick one.

**You switched React Router back from edge and it broke.** Removing `edge: true` isn’t enough; delete `app/entry.server.tsx` too.

**Hugo fails with `exit code: 255`.** A missing or mismatched `HUGO_VERSION`. Set it in `[build.environment]` to any release after 0.19. Also install themes as **git submodules** (`git submodule add ...`), not `git clone`.

**Eleventy build plugins collide.** Change `node_modules` in your `.gitignore` to `**/node_modules/**`. Otherwise Netlify plugins and Eleventy collide on `.netlify/plugins/node_modules/` and the build errors.

**Your Gatsby function exceeds the max deploy size.** Set `GATSBY_EXCLUDE_DATASTORE_FROM_BUNDLE` to load the datastore from the CDN.

**Your pnpm Nuxt 3 build fails.** Set `PNPM_FLAGS=--shamefully-hoist`.

## Reference

### Client-embed prefixes (never put secrets in these)

Framework

Prefix

Create React App

`REACT_APP_`

Gatsby

`GATSBY_`

Next.js

`NEXT_PUBLIC_`

Nuxt

`NUXT_PUBLIC_`

Vue CLI

`VUE_APP_`

Vite / Astro / SvelteKit

`VITE_`, `PUBLIC_`

### Env var scope requirements

What needs the value

Scopes required

Build command, plugins

Builds

SSR / DSG at runtime

Functions **and** Builds

Functions, Edge Functions

Functions

`netlify.toml` variables are build-time only and can’t be read by functions at runtime.

### Adapter targets

Framework

SSR target

Middleware target

Astro

Netlify Functions

Edge Functions

Next.js 13.5+

Serverless function

Edge Function

SvelteKit

`render` function (or Deno edge with `edge: true`)

React Router 7+

Serverless Functions (Deno edge opt-in)

Opt-in via `future.v8_middleware`

Nuxt 3

Nitro (Functions)

SolidStart 2

Netlify Functions

Netlify Functions

TanStack Start

Serverless functions

Serverless functions

Gatsby 5.12+

`SSR`, `DSG` functions

Angular

Edge Function

Hydrogen

**Edge Functions only**

### Deploying via CLI

```
npm install netlify-cli -gnetlify init
```

Follow the prompts to create or link the site and set build settings.

## FAQs

**How do I deploy Next.js to Netlify?** Push your repo and Netlify detects it, installing `@netlify/plugin-nextjs` automatically for Next 13.5+. Build command `next build`, publish `.next`. Don’t pin the plugin version; Netlify updates it each build.

**Which frameworks does Netlify support?** Detection and documented build settings cover Angular, Astro, Create React App, Eleventy, Gatsby, Hugo, Hydrogen, Next.js, Nuxt, React Router, Remix, SolidStart, SvelteKit, TanStack Start, Vite, Vue CLI, and Express.

**Why are my SSR pages serving static HTML instead of rendering?** You almost certainly have a leftover SPA catch-all (`/* /index.html 200`). User redirects beat adapter-generated routes. Remove it.

**Why isn’t my environment variable updating on the live site?** Values are injected at build time. Editing a variable doesn’t reach the live site or already-deployed functions until you redeploy.

**Can I test Netlify features locally without the CLI?** Yes, for Vite-based frameworks. Astro 5.12+ has it built in; Nuxt uses `@netlify/nuxt`; React Router, SolidStart, and Vite use `@netlify/vite-plugin`. That emulates functions, blobs, database, Image CDN, redirects, headers, and AI Gateway.

**Do I need the Astro adapter?** For SSR or Image CDN with `<Image />`, yes: `npx astro add netlify`. For a fully static site with no server features, you can deploy without it.

**How do I enable skew protection on Next.js?** Set `NETLIFY_NEXT_SKEW_PROTECTION=true` and redeploy. On Next.js earlier than 14.1.4 that isn’t sufficient on its own, so also set `experimental.useDeploymentId` (plus `useDeploymentIdServerActions` if you use Server Actions) in `next.config.js`.

**Is SvelteKit fully supported for local emulation?** It’s experimental. Astro, Nuxt, TanStack Start, React Router, and SolidStart are full.

**Why do my Angular redirects get ignored?** Angular SSR runs in an Edge Function that executes before redirect rules, so `_redirects` and `netlify.toml` don’t apply to SSR pages. Use Angular’s built-in redirects.

## Related

-   [Framework documentation](https://docs.netlify.com/frameworks/)
-   [Next.js v5 breaking changes](https://docs.netlify.com/build/frameworks/framework-setup-guides/nextjs/overview#v5-breaking-changes)
-   [Build configuration](https://docs.netlify.com/configure-builds/)

This article is generated from Netlify’s open-source agent guidance at [netlify/context-and-tools](https://github.com/netlify/context-and-tools), the same reference our AI coding agents use.

* * *

Ready to ship your framework? Start at [netlify.new](https://netlify.new).