> ## Documentation Index
> Fetch the complete documentation index at: https://docs.edgespark.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Build auth UI with @edgespark/web

> Build login, signup, and session-aware frontend flows in EdgeSpark with the @edgespark/web browser SDK and managed auth UI.

The current EdgeSpark scaffold uses `@edgespark/web` for frontend auth and API calls. It is a browser-only, same-origin SDK with cookie-based sessions, a managed auth UI, and a typed fetch wrapper for your app routes.

## Create the browser client

The scaffold already creates a singleton in `web/src/lib/edgespark.ts`:

```typescript web/src/lib/edgespark.ts theme={null}
import { createEdgeSpark } from "@edgespark/web";
import "@edgespark/web/styles.css";

export const client = createEdgeSpark();
```

`createEdgeSpark()` takes no options. It always uses the current page origin and always sends cookie credentials.

## Mount the managed auth UI

Use `client.authUI.mount(...)` when you want EdgeSpark to handle sign-in, sign-up, email verification, OAuth entry points, and password reset UI for you.

### Redirect mode

Use redirect mode for a dedicated `/login` page:

```tsx web/src/pages/LoginPage.tsx theme={null}
import { useEffect, useRef } from "react";
import { client } from "@/lib/edgespark";

export function LoginPage() {
  const containerRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    const mounted = client.authUI.mount(containerRef.current, {
      redirectTo: "/dashboard",
    });

    return () => {
      mounted.destroy();
    };
  }, []);

  return <div ref={containerRef} />;
}
```

After a successful sign-in or sign-up, the SDK redirects to `/dashboard`.

### Controlled mode

Use controlled mode when the app owns the next step, such as closing a modal or navigating with a client router:

```tsx web/src/components/AuthModal.tsx theme={null}
import { useEffect, useRef } from "react";
import { client } from "@/lib/edgespark";

interface AuthModalProps {
  onClose(): void;
}

export function AuthModal({ onClose }: AuthModalProps) {
  const containerRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    const mounted = client.authUI.mount(containerRef.current, {
      onSuccess(event) {
        if (event.action !== "password-reset") {
          onClose();
        }
      },
      onError(error) {
        console.error(error.code, error.message);
      },
    });

    return () => {
      mounted.destroy();
    };
  }, [onClose]);

  return <div ref={containerRef} />;
}
```

In controlled mode, the SDK does not redirect for you.

## Use headless auth methods

You can also call auth methods directly when you want custom forms:

```typescript web/src/lib/auth.ts theme={null}
import { client } from "@/lib/edgespark";

export async function signIn(email: string, password: string) {
  const result = await client.auth.signIn.email({ email, password });

  if (result.error) {
    throw new Error(result.error.message ?? "Sign-in failed");
  }

  return result.data;
}

export async function signUp(name: string, email: string, password: string) {
  const result = await client.auth.signUp.email({ name, email, password });

  if (result.error) {
    throw new Error(result.error.message ?? "Sign-up failed");
  }

  return result.data;
}
```

The same client also exposes:

* `client.auth.getSession()` for nullable session reads
* `client.auth.requireSession()` for route guards that should throw when logged out
* `client.auth.onSessionChange(...)` for hydration-safe session subscriptions
* `client.auth.signOut()` for logout

## Call your protected APIs

Use `client.api.fetch(...)` for same-origin app requests:

```typescript web/src/lib/load-posts.ts theme={null}
import { client } from "@/lib/edgespark";

export async function loadPosts() {
  const response = await client.api.fetch("/api/posts");

  if (!response.ok) {
    throw new Error(`Request failed: ${response.status}`);
  }

  return response.json();
}
```

The wrapper always sends cookie credentials and rejects cross-origin URLs.

## Let auth config drive the UI

The managed auth UI reads your project's current auth configuration automatically. That means:

* enabled OAuth providers appear automatically
* sign-up availability follows `disableSignUp`
* email verification and password reset behavior follow `configs/auth-config.yaml`

When you change auth config, use:

```bash theme={null}
edgespark auth apply
edgespark deploy
```

See [manage auth configuration](/guides/auth-config) for the config file format and provider key conventions, or [add social OAuth login](/guides/social-login) for per-provider setup walkthroughs.

## See also

<Columns cols={2}>
  <Card title="web reference" icon="code" href="/sdk/web">
    The browser SDK reference for `@edgespark/web`, including `auth`, `authUI`, and `api.fetch`.
  </Card>

  <Card title="Manage auth configuration" icon="sliders-horizontal" href="/guides/auth-config">
    Configure providers, sign-up policy, session settings, vars, and secret refs in `configs/auth-config.yaml`.
  </Card>
</Columns>
