# Frontend

Source: https://developers.swell.is/apps/frontend

Swell App **frontends** can serve different purposes, depending on the goal of the app. A frontend is essentially its own application within a Swell App, intended to render pages for public display. For example, a storefront app implements the page structure and functionality of a storefront using a frontend. Frontends run as Cloudflare Workers, either hosted by Swell or deployed to your own Cloudflare account.

You can create a new frontend with the following CLI command. It prompts for a frontend template, and sets `frontend.hosting` in swell.json to the template's hosting mode. You can also add a compatible framework to the app's `frontend/` folder yourself.

```txt
swell create frontend
```

To skip the prompt, pass a template, for example `swell create frontend --frontend swell-react -y`. See Frameworks for the templates.

### Use cases

- **Storefront apps**: Customer interfaces such as product pages, checkout, account portals, and more.
- **Admin apps**: Merchant user interfaces that are embedded in the Swell dashboard.
- **Integrations**: Configuration or other interstitial interfaces to support unique workflows, serving either merchants or customers.

#### Storefronts

[Proxima app](https://developers.swell.is/storefronts/proxima-app) serves as an example for developers to learn best practices for developing storefront frontends, but the sky is the limit. With a wide range of framework choices, any number of storefront use cases can be achieved on the Swell platform. The React SPA Storefront template, `swell-spa`, starts a new storefront app on managed hosting.

#### Admin apps and integrations

A frontend can be embedded in Swell's dashboard, to provide a more flexible interface for merchants to manage data and settings for your app. Links with the `frontend://` scheme in content views, navigation and actions open the app's frontend inside the dashboard. A store user who opens it this way is identified by the `admin` claim of `Swell-Context`. See Swell API access.

### Frameworks

A frontend is either managed, hosted by Swell, or self-hosted in your own Cloudflare account. Set the mode with `frontend.hosting` in swell.json. When it isn't set, the frontend is self-hosted.

- `managed`: Swell deploys the frontend on its own Cloudflare account, so you don't need one. Managed frontends support Vinext, and client-side React with Vite.
- `self-hosted`: the CLI deploys the frontend with Wrangler to your own Cloudflare account. Self-hosted frontends support Next.js with OpenNext, Astro, Nuxt, React with Vite, Hono and Angular.

Next.js apps must be self-hosted. To move a managed frontend to self-hosted, set `frontend.hosting` to `self-hosted`. Removing the key isn't enough.

#### Templates

`swell create app` and `swell create frontend` accept the following templates with `--frontend`. The managed templates require Node.js 22.22.2 or later.

| Template | Framework | Hosting |
| --- | --- | --- |
| swell-react | React and Vite, with client rendering and server endpoints | managed |
| swell-vinext | Vinext, with server rendering | managed |
| swell-spa | React SPA Storefront, for storefront apps | managed |
| nextjs | Next.js | self-hosted |
| astro | Astro | self-hosted |
| nuxt | Nuxt | self-hosted |
| react | React and Vite | self-hosted |
| hono | Hono | self-hosted |
| angular | Angular | self-hosted |

We will expand this list as demand for additional support grows. If your framework of choice is not yet supported, reach out to us or make a pull-request on Github.

### Swell Apps SDK

To simplify your frontend's interactions with the Swell platform, we've developed the Apps SDK, a Typescript-based library that comes built-in with frontend and backend API clients, and theme rendering logic for storefront apps.

→ See the [Apps SDK reference](https://developers.swell.is/apps/swell-apps-sdk) for more details.

### Local development

During local development, you'll be able to run the app on your machine and quickly preview changes using the following CLI command:

```txt
swell app dev
```

This will perform several functions:

- Start a local proxy through a Cloudflare tunnel.
- Start the app using its own `dev` command, for example `next dev` or `astro dev`.
- Add the local proxy URL to your account session, which informs Swell that your app dev server is running and should be loaded instead of a deployed version.
- Note: This command will also start a development server for app [functions](https://developers.swell.is/apps/functions), if any exist.

*You should only run* *`app dev`* *on one app at a time, otherwise your account session will only remember the last app that was started.*

When the command is successfully executed, it prints a URL to preview the app through the proxy. It also watches for changes and automatically pushes configurations and files to Swell as you develop the app. The preview always uses your local dev server, for both managed and self-hosted frontends.

### Deployment

When you're ready to test your frontend in the cloud, or to make a final deployment before release, deploy it with the CLI. How the CLI deploys depends on `frontend.hosting`:

- Managed: the CLI builds the frontend with `vinext build` or `vite build`, packages the Worker and its static assets, and uploads the package to Swell, which deploys it. The CLI rebuilds the frontend on every deploy.
- Self-hosted: the CLI runs the framework's build command and deploys the frontend with Wrangler to your own Cloudflare account. It skips the deploy when the frontend hasn't changed, unless you pass `--force`.

For a self-hosted frontend, first log in to Cloudflare using wrangler:

```txt
wrangler login
```

In a non-interactive shell, also export `CLOUDFLARE_ACCOUNT_ID`. Then deploy the frontend, in either hosting mode, with the following command:

```txt
swell app frontend deploy
```

> **Tip:** `swell app push` also deploys the frontend after pushing the app's configuration, and so does pushing a path inside `frontend/`. Pass `--no-deploy` to skip it.

Admin and integration app frontends are served at `https://<store-id>--<installation-id>--app.swell.store`, and storefront app frontends on the store's storefront domains. `swell app push` and `swell app frontend deploy` update the app in the store's test environment. A released app version keeps the frontend it was released with.

#### Reserved paths

Swell handles some paths on an app's domain before they reach the frontend, including `/api`, `/graphql`, `/functions` and `/.well-known`. On storefronts, `/checkout` goes to Swell's hosted checkout unless the store uses a custom checkout. Serve your app's own endpoints under `/app-api`, which Swell passes to the frontend with the path, query and body unchanged.

#### Managed frontends

- Only the `ASSETS` binding is available. KV, D1, R2, Durable Objects, queues, service bindings, environment variables and secrets aren't. Use app settings for configuration.
- The compatibility date is fixed at `2026-09-08`, with no compatibility flags. Node.js built-in modules are available at this date.
- npm dependencies must be bundled into the Worker. The CLI reports any that aren't.
- A package can contain up to 5,000 files, with a total size of up to 5 MiB.
- Swell doesn't cache responses on a CDN. Browsers cache responses that set `Cache-Control` with `max-age` and `immutable`.
- Worker logs aren't available in `swell logs`. Use your local dev server's logs and the deployment errors to diagnose issues.

Managed deployments return errors with the following codes. A failed deployment leaves the previous frontend serving.

| Code | Meaning |
| --- | --- |
| frontend_package_invalid | The package failed validation. The message gives the reason. |
| frontend_deployment_failed | Cloudflare rejected the upload. Correct the reported error and deploy again. |
| frontend_package_inactive | A version was created while the selected package wasn't the active deployment. Deploy the package first. |
| frontend_url_check_failed | The URL of a self-hosted frontend failed its check when switching from managed hosting. The managed frontend stays active. |
| frontend_deployment_busy | Another operation is updating the app. Try again shortly. |
| frontend_deployment_changed | The deployment changed during the operation. Deploy again. |
| frontend_selection_changed | The app's frontend selection changed during the operation. Try again. |
| frontend_activation_failed | The app update couldn't be completed. Try again shortly. |

## Swell API access

When your app is invoked, either as a storefront or as an admin app, Swell will proxy the request along with headers that can be used by the app to identify the merchant and authenticate with the store's frontend and backend APIs.

The following table outlines the headers passed to a frontend app when requested:

| Header | Description |
| --- | --- |
| Swell-Context | Signed JSON Web Token identifying the store, app, installation and store user for the request. See Verifying requests. |
| Swell-Store-Id | ID of the store. |
| Swell-Environment-Id | String 'test' indicates the request is from a test environment, while blank indicates a live environment. |
| Swell-App-Id | ID of the app, as set by `id` in swell.json. |
| Swell-App-Version | Version of the app, i.e. "1.0.0". Not sent for apps in development. |
| Swell-App-Route | Set to `/<app-private-id>` when the request path started with the app's private ID. Swell removes that prefix before forwarding the request. |
| Swell-Access-Token | Unique backend API key representing scoped access for the app installed in a merchant's store. Keep it on the server. |
| Swell-Public-Key | Unique frontend API key representing scoped access for the app installed in a merchant's store. |
| Swell-API-Host | URL of the backend API, such as `https://api.swell.store`. |
| Swell-Admin-Url | Base URL of the store, such as `https://<store-id>.swell.store`. |

**Storefront-specific headers:**

| Header | Description |
| --- | --- |
| Swell-Storefront-Id | ID of a storefront using this app, if applicable. |
| Swell-Deployment-Mode | Indicates the storefront context for the request. One of `editor`, `preview`, or `live`. |
| Swell-Cache-Modified | Date the storefront was last modified or published, relative to this request. |
| Swell-Storefront-Host | Domain of the request, without `www.` or the port. |
| Swell-Storefront-Context | URL-encoded JSON with the visitor's `account` and `cart`, when it fits in the request headers. |

**Theme-specific headers:**

| Header | Description |
| --- | --- |
| Swell-Theme-Id | ID of a theme that should be loaded by this app. |
| Swell-Theme-Version | Version of the theme, i.e. "1.0.0". |
| Swell-Theme-Version-Hash | Unique hash of all theme files combined, often used to cache response output in association with other properties. |
| Swell-Theme-Config-Version | Version of the theme configuration: `preview-<n>` for previews, or `live-<n>` for the published configuration. |

These headers are unique for each store and its app installation. Once invoked, your frontend will likely connect to the Swell [Frontend API](https://developers.swell.is/frontend-api/introduction), [Backend API](https://developers.swell.is/backend-api/introduction), or both, using the access token (backend) and public key (frontend) provided via headers. Keep the access token on the server, and send only the public key to the browser.

**Example**

Using the [Apps SDK](https://developers.swell.is/apps/swell-apps-sdk):

**Apps SDK**

**Node**

```javascript
import { Swell } from '@swell/apps-sdk';

const swell = new Swell({
  serverHeaders: context.request.headers, // Object received from worker environment
  ...options,
});

// Make a backend API call
await swell.backend.get('/products');

// Make a frontend API call
await swell.storefront.get('/products');
```

Using Swell libraries:

**Swell libraries**

**Node**

```javascript
import Swell from 'swell-node';
import SwellJS from 'swell-js';

const storeId = context.request.headers['Swell-Store-Id'];
const accessToken = context.request.headers['Swell-Access-Token'];
const publicKey = context.request.headers['Swell-Public-Key'];

// Initialize backend API client
const backend = Swell.init(storeId, accessToken, [...options]);

// Initialize frontend API client
const storefront = SwellJS.create(storeId, publicKey, [...options]);
```

You may otherwise prefer to build your own request handlers using the Fetch API, following our backend and frontend API guides for details.

### Verifying requests

`Swell-Context` is a JSON Web Token signed by Swell with ES256, and expires 60 seconds after it's issued. Only trust the store, app or store user it identifies after verifying it. The other `Swell-*` headers aren't signed.

Verify the signature with the key from https://swell.store/.well-known/jwks.json that matches the token's `kid`. Then check that `iss` is `https://swell.store`, `aud` is your app's ID, and the token hasn't expired. The token contains:

- `store_id`, `environment_id` (`test`, or `null` for live), `app_id`, `installation_id` and `storefront_id`
- `api_host` and `admin_url`
- `admin`: `{ "user_id": "<id>" }` when a store user opened the frontend from the dashboard, otherwise `null`

Use the `admin` claim to identify the store user, rather than the `_swell_admin_session` cookie, which is temporary.

## Cloudflare context

Because frontends are hosted by Cloudflare Workers, your app has access to standard worker context properties and runtimes. Managed frontends have the limits listed under Managed frontends.

Here are some useful docs to better understand the worker environment:

- Framework guides
- Node.js compatibility
- Runtime APIs
- Workers KV (self-hosted frontends only)

> **Tip:** For self-hosted frontends, Cloudflare KV is recommended for caching output to optimize app performance.

**Things to consider:**

- The Cloudflare Worker environment supports a limited subset of the Node.js runtime. Some npm packages may rely on APIs that are unavailable in this context.
- Some npm libraries may work on your local machine, however due to the difference in local vs Worker Node.js environments, it's important to test cloud deployments regularly.
- When deploying a storefront app on your own Cloudflare account, it is your responsibility to maintain the account in order for the app to remain accessible to users.
