# Standalone React Front End

> Build decoupled React single-page applications with Vite, session authentication, API integration, and deployment to static hosting platforms.

Experimental Feature

This feature is experimental. It is likely (but not guaranteed) that it will make it into a future Pegasus release. While in the experimental phase, it may undergo significant changes, including breaking changes.

*Added in version 2024.4. Expanded in versions 2025.4.1 and 2026.1.*

SaaS Pegasus’s default React integration is based on a hybrid-model for reasons [outlined here](https://www.saaspegasus.com/guides/modern-javascript-for-django-developers/client-server-architectures/#enter-the-hybrid-architecture). The hybrid model is still recommended for the overwhelming majority of Pegasus projects using React. However, there are valid reasons to run a completely separate React front---including access to dedicated tooling and libraries, isolating your front end and back end code, and working with AI-based tools that generate single-page applications.

Pegasus experimentally ships with a decoupled front end *example* single page application that can be used as a starting point for building out a decoupled front end / SPA with React. It uses [Vite](https://vitejs.dev/) as a development server and build tool, and uses React with TailwindCSS and [shadcn/ui](https://ui.shadcn.com/) for components.

The features it includes are:

* A standalone Vite / React application.
* Authentication via headless allauth and sessions---including sign up, login, social login, email confirmation, two-factor authentication, and logout functionality.
* A shadcn/ui component environment setup with DaisyUI theme colors.
* A sample profile page which shows how to retrieve data from the logged in user (via the back end APIs) and display it.
* The employee lifecycle demo that ships with Pegasus (if enabled), showing a full create, update, delete (CRUD) workflow.

The standalone front end is *only available on TailwindCSS*.

**The standalone is not intended to be a replacement for Pegasus’s UI, but a reference example you can use as a starting point to build standalone, single-page-applications with Pegasus and React.**

Here’s a demo:

And here are some technical details:

## Running the front end

[Section titled “Running the front end”](#running-the-front-end)

*If you are using Docker, your front end should start in a separate container after running `make init`.*

The front end lives in the `/frontend` folder of your project. To set it up for the first time, first go into the directory:

```bash
cd frontend
```

And install npm packages:

```bash
npm install
```

Create your `.env` file:

```bash
cp .env.example .env
```

Then run the dev server:

```bash
npm run dev
```

Note: your Django backend must also be running for the front end to work, and you must also [build your Django front end](/front-end/overview) for styles to work.

## Shadcn setup

[Section titled “Shadcn setup”](#shadcn-setup)

This frontend uses [shadcn/ui](https://ui.shadcn.com/) for UI components. shadcn components are copied into the codebase (not installed as a package) and can be customized directly.

### Adding Components

[Section titled “Adding Components”](#adding-components)

```bash
cd frontend
npx shadcn@latest add [component-name]
```

Components are installed to `src/components/ui/`. Browse available components at <https://ui.shadcn.com/docs/components>.

### Key Files

[Section titled “Key Files”](#key-files)

* `components.json` - shadcn configuration
* `src/lib/utils.ts` - `cn()` helper for className merging
* `src/components/ui/` - all shadcn components
* `src/index.css` - CSS variables and DaisyUI color mapping

### DaisyUI Integration

[Section titled “DaisyUI Integration”](#daisyui-integration)

shadcn colors are mapped to DaisyUI theme variables in `src/index.css` under `@theme inline`. This ensures shadcn components use the same colors as the rest of the app.

Key mappings:

* `--color-primary` → DaisyUI primary
* `--color-destructive` → DaisyUI error
* `--color-muted` → DaisyUI base-200
* `--color-accent` → DaisyUI base-300

## Authentication

[Section titled “Authentication”](#authentication)

Authentication uses session-based authentication against the Django backend (previous versions of the front end used JWTs). The authentication implementation borrows heavily from the [allauth example](https://github.com/pennersr/django-allauth/tree/main/examples/react-spa) project. In particular, the `src/lib/` and `src/allauth_auth/` folders have been copied in from that project and lightly modified to work with Pegasus.

Authentication is primarily handled via *authenticated routes* and *authentication context*. You can see an example of how to set this up in the profile page.

Any page in your application that requires login can be wrapped in the `AuthenticatedRoute` component. For example, like this:

```jsx
<AuthenticatedRoute>
  <p>Hello authenticated user!</p>
</AuthenticatedRoute>
```

Alternatively, if you make a page a child of the `<Dashboard>` component this will be automatically configured for you. See `main.tsx` as an example of how this is set up.

When using the `AuthenticatedRoute`, if the user is not logged in they will be redirected to the login page. If they are logged in, they will be able to access the route, and you can assume access to the user object and other APIs that require login.

If you want to access user data you can use the `useAuthInfo` helper function which returns an `AuthContext` context. Here is a simplified example taken from the Profile page:

```jsx
import { useAuthInfo } from "../../allauth_auth/hooks";


export default function Profile() {
  const { user } = useAuthInfo();
  return <p>The user's email address is: {user?.email}</p>
}
```

## Backend API access

[Section titled “Backend API access”](#backend-api-access)

The front end uses the [same api client](/apis/#api-clients) as the backend / hybrid model. The API client is installed as a local npm package.

Authentication is handled via sessions and does not require any additional configuration. Here is a basic example from the employee app demo:

```jsx
import {PegasusApi} from "api-client";
import EmployeeApplication from "../../../../assets/javascript/pegasus/examples/react/App.jsx";
import {getApiConfiguration} from "../../api/utils.tsx";


export default function EmployeeApp() {
  const client = new PegasusApi(getApiConfiguration());
  return (
    <EmployeeApplication client={client} urlBase="/dashboard/employees" />
  );
}
```

## Routing

[Section titled “Routing”](#routing)

Routing is handled by [React Router](https://reactrouter.com/en/main).

The main routes for the project are configured in `main.tsx`, and you can also include child routes by following the pattern used by the employee demo.

## URLs in Emails

[Section titled “URLs in Emails”](#urls-in-emails)

Some workflows, like email confirmation and password reset, require sending the user a link to your site. Allauth only supports a single link for the entire application so you need to choose whether that link should go to your Django application or your React front end.

To use the React front end’s pages for these workflows, you can set `USE_HEADLESS_URLS = True` in your settings or environment variables. This will configure the [`HEADLESS_FRONTEND_URLS` setting](https://docs.allauth.org/en/dev/headless/configuration.html) to work with the built-in front end.

## Deployment

[Section titled “Deployment”](#deployment)

Big picture, you should deploy the standalone front end and Django backend separately, and use different subdomains to point to them. The most common set up is to deploy the front end to either “mydomain.com” or “[www.mydomain.com](http://www.mydomain.com)”, and then deploy the backend to “app.mydomain.com” or “platform.mydomain.com”.

### The Django Backend

[Section titled “The Django Backend”](#the-django-backend)

You will need to deploy your Django backend using any of the [standard deployment methods](/deployment/overview).

In addition to a standard deployment, you will specifically need to set the following additional settings, by overriding them in your environment variables or a production settings file:

* `FRONTEND_ADDRESS`: Your front end’s full URL, e.g. “<https://www.mydomain.com>”
* `CORS_ALLOWED_ORIGINS`: Full URLs of both your frontend and backend addresses, e.g. “<https://www.mydomain.com,https://app.mydomain.com>”
* `CSRF_COOKIE_DOMAIN`: All domains and subdomains, e.g. “.mydomain.com” (note the leading ”.”).
* `SESSION_COOKIE_DOMAIN`: Same as `CSRF_COOKIE_DOMAIN`.

### The React Frontend

[Section titled “The React Frontend”](#the-react-frontend)

The frontend can be deployed anywhere that hosts static sites, including Cloudflare Pages, Netlify, or S3.

The basic steps for deployment are to run `npm run build` and then serve the output directory as a static site.

In addition, the following environment variables need to be set during build. Do not include trailing slashes:

* `VITE_APP_BASE_URL`: Your django backend url, e.g. “<https://app.mydomain.com>”
* `VITE_ALLAUTH_BASE_URL`: The full allauth base route for your backend, e.g. “<https://app.mydomain.com/_allauth/browser/v1>”

Each static site host has their own way of configuring the above setup. Below are quick example instructions for deploying the front end on Cloudflare Pages:

1. In the Cloudflare dashboard, visit “Workers & Pages” and click “Create”

2. Under “pages”, select the option to connect a Github repository.

3. Pick your Pegasus Github repository. You may have to authenticate and provide access permissions.

4. Fill in the following settings:

   1. Build command: `npm run build`

   2. Build output directory: `dist`

   3. Root directory: `frontend`

   4. Add the following environment variables. *Note that the URLs should not end in slashes.*

      * `VITE_APP_BASE_URL: https://<your Django backend URL>`
      * `VITE_ALLAUTH_BASE_URL: https://<your Django backend URL>/_allauth/browser/v1`
      * `NODE_VERSION: 22.13.0`
      * `NPM_VERSION: 11.3.0`

5. Click “Save and Deploy”

6. After the initial deployment you can add a custom domain to your front end.

## Known Limitations

[Section titled “Known Limitations”](#known-limitations)

This is an experimental feature meant to provide a starting point for building a standalone React front end against your Pegasus app. It is *not* a complete, production-ready app, in the same way that standard Pegasus is.

Here are some of the larger limitations:

* Only a very limited subset of Pegasus functionality is available in the front end.
* The front end styles only support Tailwind CSS.
* Internationalization (translations) are not supported.

## Troubleshooting

[Section titled “Troubleshooting”](#troubleshooting)

**I’m getting a “URI malformed” error when I load the app.**

This is likely because your `frontend/.env` file does not exist, or your `VITE_APP_BASE_URL` is not properly set inside it. See `frontend/.env.example` for an example `.env` file suitable for development.

## Feedback

[Section titled “Feedback”](#feedback)

If you have any feedback on this feature I would love to hear it! Feedback could include bug reports, feature requests, or any suggested architectural changes.