DeployEdit this page

Azure Functions

To deploy to Azure Functions, use the built-in azure adapter. This adapter uses the Azure Functions v4 programming model with full response streaming support.

You need the following tools installed:

# Install Azure Functions Core Tools npm install -g azure-functions-core-tools@4 --unsafe-perm true # Install Azure CLI (macOS) brew install azure-cli # Login to Azure az login

No additional packages are needed — the adapter is built into @lazarv/react-server.

Add the adapter to your react-server.config.mjs file:

export default { adapter: "azure", };

You can customize the adapter by passing options:

export default { adapter: [ "azure", { appName: "my-app", // Azure Function App name resourceGroup: "my-rg", // Azure resource group location: "westus2", // Azure region storageName: "myappstorage", // Storage account name host: {}, // Extra host.json properties env: { // Extra environment variables MY_API_KEY: "value", }, }, ], };

Configuration Options

Build and deploy in one step:

pnpm react-server build --deploy

Or build first and deploy manually:

# Build pnpm react-server build # Deploy using Azure Functions Core Tools func azure functionapp publish <app-name> --javascript

Note: When deploying manually with func, you must run the command from the .azure/ output directory.

When you deploy with --deploy, the adapter automatically provisions all required Azure resources using a Bicep template:

If the Function App already exists (in any resource group), provisioning is skipped entirely. The adapter searches across all resource groups in your subscription to find existing apps.

The generated main.bicep template is written to the .azure/ directory. You can inspect or customize it before deploying manually with:

az deployment group create \ --resource-group <resource-group> \ --template-file .azure/main.bicep \ --parameters appName=<app-name> storageName=<storage-name> location=<region>

This adapter fully supports response streaming. React Server Components, Suspense boundaries, and progressive HTML delivery all work out of the box. The Azure Functions v4 programming model's app.http() handler supports returning ReadableStream bodies, enabling true streaming without buffering.

This is a key advantage over Azure Static Web Apps, which buffers all responses.

The adapter uses an edge build mode, bundling your entire server into a single file. At build time, it:

  1. Bundles your server into .azure/server/.react-server/server/edge.mjs
  2. Copies all static assets into .azure/static/
  3. Generates a thin src/functions/server.mjs wrapper that:
    • Imports @azure/functions (kept external — required by the Azure runtime)
    • Registers an HTTP trigger via app.http() matching all methods and paths
    • Serves static files from a build-time route map using readFileSync()
    • Delegates dynamic requests to the bundled edge handler
  4. Generates host.json, package.json, local.settings.json, and main.bicep
  5. Runs npm install in .azure/ to install @azure/functions

Important: The @azure/functions package cannot be bundled into the edge build. The Azure Functions runtime provides its own instance of this module and uses it to discover registered HTTP triggers. Bundling it would cause the runtime to see zero registered functions.

After building, you can test locally using Azure Functions Core Tools:

cd .azure func start

The generated local.settings.json configures the local runtime. If you built with --sourcemap, Node.js source maps are also enabled via NODE_OPTIONS.

.azure/ ├── host.json # Azure Functions host configuration ├── local.settings.json # Local development settings ├── main.bicep # Bicep IaC template for provisioning ├── package.json # Dependencies (@azure/functions) ├── node_modules/ # Installed dependencies ├── server/ │ └── .react-server/ # Bundled server (edge.mjs, manifests) ├── src/ │ └── functions/ │ └── server.mjs # Azure Functions v4 HTTP trigger wrapper └── static/ # Static assets (HTML, CSS, JS, images)

"SubscriptionIsOverQuotaForSku" error

This means your Azure subscription doesn't have quota for Dynamic (consumption) VMs in the selected region. Try a different region:

export default { adapter: ["azure", { location: "westus2" }], };

Or request a quota increase in the Azure portal.

"Unable to find project root" from func

The func CLI must run from the directory containing host.json. When deploying manually, make sure you cd .azure first. The --deploy flag handles this automatically.

Empty function list after deployment

If func publish succeeds but reports no functions, this usually means @azure/functions was bundled instead of kept external. Verify that .azure/node_modules/@azure/functions/ exists. The adapter handles this automatically, but if you've customized the build, ensure the package remains external.

Functions respond with 404 at /api/...

The adapter sets routePrefix: "" in host.json to remove the default /api/ prefix. If you see 404s at paths like /api/server, check that your host.json wasn't overwritten or that the host adapter option isn't re-adding the prefix.

Bicep deployment fails

The full Azure error is displayed in the build output. Common causes:

Azure CLI not logged in

If you see "Azure CLI is not installed or you are not logged in", run:

az login az account show # Verify you're logged in

Slow cold starts

Azure Functions on the Consumption plan may have cold starts of a few seconds. For lower latency, consider upgrading to a Premium plan by customizing the Bicep template in .azure/main.bicep.