---
name: zitadel-sdk-qwik
description: >-
  Integrate Zitadel authentication into a Qwik app with `@zitadel/sdk-qwik`.
  Use when the user wants to add login, logout, or a signed-in session card to
  an existing or custom client-side Qwik SPA — wiring the `ZitadelLogin`,
  `ZitadelLogout`, or `ZitadelSession` components and `configureZitadel`.
---

# @zitadel/sdk-qwik

Qwik components that wrap the Zitadel auth UI web components (`<zitadel-login>`,
`<zitadel-logout>`, `<zitadel-session>`) for client-side SPAs (Vite, etc.). The
package exports the components, the `configureZitadel` handle factory, and the
shared SPA config/handler types.

## When to use

- **Fresh Qwik app → use the CLI instead.** `npx @zitadel/cli@alpha setup --framework qwik`
  scaffolds auth into a new or detected Qwik project end to end (it wires this
  SDK, the dev proxy, and config). Reach for the `zitadel-cli` skill for that.
  Note the CLI scaffolds against **Qwik 2**, which this package also targets.
- **This skill is for an existing or custom Qwik app** where you are adding the
  components by hand — or adjusting a wiring the CLI already produced.

## Install

```sh
pnpm add @zitadel/sdk-qwik@alpha
```

The package declares a peer dependency on `@qwik.dev/core` (`^2.0.0-beta.45`),
so the host app must be on **Qwik 2**. TypeScript ≥ 5.0 is required — the
published types re-export with `export type *`, a 5.0 feature.

## Integrate

Build a project handle with `configureZitadel`, then render a component with it:

```tsx
import { component$ } from '@qwik.dev/core';
import { ZitadelLogin, configureZitadel } from '@zitadel/sdk-qwik';

export default component$(() => {
  const project = configureZitadel({
    projectId: import.meta.env.VITE_ZITADEL_PROJECT_ID,
    proxyPath: '/__nextgen',
  });

  return <ZitadelLogin project={project} purpose="login" postSignInUrl="/" />;
});
```

- `<ZitadelLogout project={project} postSignOutUrl="/login" />` works the same way.
- `<ZitadelSession project={project} />` renders the signed-in session card.
- Instead of the `project` handle you may pass the discrete `projectId` /
  `proxyPath` props; the widget uses whichever is present.
- Widget events are optional QRL callbacks; see Widget props and events.
- The SDK binds `project` (and `projectId` / `proxyPath`) to the element as DOM
  *properties*, matching the other framework SDKs.

## Widget props and events

`ZitadelLogin`, `ZitadelLogout` and `ZitadelSession` take the props below, spelled the same in every SPA SDK. An unset prop keeps the widget's own default.

| Prop | Type | Default | Widgets | Description |
| --- | --- | --- | --- | --- |
| `project` | `ZitadelProject` | unset | Login, Logout, Session | The handle from `configureZitadel(...)`. Wins over `projectId` and `proxyPath`. |
| `projectId` | `string` | unset | Login, Logout, Session | Project id, read when no `project` handle is passed. |
| `proxyPath` | `string` | `"/__nextgen"` | Login, Logout, Session | Same-origin path the widget calls. |
| `purpose` | `CreateFlowBodyPurpose` | `"login"` | Login | Flow purpose. |
| `flowName` | `string` | unset | Login | Runs the flow definition with this `name`; unset runs the project's default flow for the purpose. |
| `postSignInUrl` | `string` | unset | Login | Where to navigate after a completed sign-in. |
| `postSignOutUrl` | `string` | unset | Logout, Session | Where to navigate after sign-out. |
| `resumeFlowId` | `string` or `null` | unset | Login | An existing flow's id to resume instead of starting a new flow; unset, `null` or `""` falls back to the `flow` query parameter, and an id that no longer resolves starts a new flow. |
| `variant` | `"widget"` or `"page"` | `"widget"` | Login, Session | `widget` is content-sized and paints no page chrome; `page` claims the viewport, paints the surface background and loads the brand font, and on Login focuses the first field. |
| `theme` | `"light"`, `"dark"` or `"auto"` | unset | Login, Logout, Session | Colour mode. Unset follows the tenant's branding theme on Login, then the variant default (`dark` for `page`, `auto` for `widget`); Logout defaults to `auto`. `auto` follows `prefers-color-scheme`. |
| `suppressHeader` | `boolean` | `false` | Login, Session | Visually hides the widget's own heading block while keeping it in the accessibility tree, for pages that already carry the heading. |
| `suppressPasswordToggle` | `boolean` | `false` | Login | Hides the show/hide button on password fields. |
| `heading` | `string` | `"Signed in as"` | Session | Heading text override. |
| `logoutLabel` | `string` | `"Sign out"` | Session | Logout action label override. |
| `locales` | `Record<string, Partial<Record<string, string>>>` | unset | Login | Copy overrides merged over the builtin dictionaries, keyed by primary language subtag, for example `{ en: { "identifier.title": "Welcome back" } }`. |
| `lang` | `string` | browser language | Login | BCP-47 language override. |

Each widget event reaches the callback in the last column, which receives the event's `detail` payload.

| Widget event | Fires when | Detail | Widgets | Callback |
| --- | --- | --- | --- | --- |
| `zitadel-flow-step` | Login shows a step, the first one included. | `{ step }` | Login | `onFlowStep$` |
| `zitadel-flow-input` | The visitor edits a field. | `{ name, value }` | Login | `onFlowInput$` |
| `zitadel-flow-complete` | The flow reaches a terminal step. | `{ behavior, redirect_uri?, handoff_token?, handoff_token_expires_at? }` | Login | `onFlowComplete$` |
| `zitadel-flow-redirect` | The widget hands the browser to an identity provider; nobody is signed in yet. | `{ redirect_url, step }` | Login | `onFlowRedirect$` |
| `zitadel-flow-error` | The flow reports an error. | `{ message }` | Login | `onFlowError$` |
| `zitadel-signout` | The user signs out. | `{ display, identifier }` | Logout, Session | `onSignout$` |

```tsx
<ZitadelLogin project={project} theme="light" onFlowComplete$={(detail) => track(detail)} />
```

## Read these, don't guess

Confirm the current surface before wiring — do not rely on memory:

- `node_modules/@zitadel/sdk-qwik/README.md` — the installed package's own usage
  notes (proxying, deployment status) are authoritative over this skill.
- The package's `.d.ts` types (`node_modules/@zitadel/sdk-qwik/dist/index.d.ts`)
  are the source of truth for exports and prop names. The named exports are
  `ZitadelLogin`, `ZitadelLogout`, `ZitadelSession`, `configureZitadel`,
  `getApi`, `getZitadelConfig`, and `businessLocales`; prop types
  (`ZitadelLoginProps`, etc.) and the shared SPA config/handler types are
  re-exported there. Check props (`purpose`, `variant`, `theme`, `flowName`,
  `locales`, `lang`, `ref`, …) against the types rather than assuming them.

## Gotchas

- **Server rendering.** If your app renders on the server, mount the widget on the client only, after hydration (the `ZitadelLogin` wrapper sets `projectId` and `proxyPath` as element properties, so adding them to the wrapper does not put them in server markup), or use a raw `<zitadel-login>` element with `project-id` and `proxy-path` as attributes, because a widget that starts before its configuration arrives shows an error instead of the flow.
- **Qwik 2 only.** The peer dep is `@qwik.dev/core ^2.0.0-beta.45` and imports
  come from `@qwik.dev/core` (not `@builder.io/qwik`). A Qwik 1 app will not
  satisfy the peer range.
- **Proxying is same-origin.** The widgets call `${proxyPath}/…` same-origin
  (default `/__nextgen`). The CLI dev proxy covers local development. Per the
  README, **production SPA deployment is not yet supported** — the same-origin
  path must come from your hosting platform, and the CLI cannot scaffold those
  configs yet.
- **Local dev without the proxy.** You can point `proxyPath` straight at the
  backend cross-origin, e.g. `proxyPath: 'http://localhost:8080'` (the
  `zitadel start` local runtime default port).
- **Properties, not attributes.** Config is applied imperatively as DOM
  properties because Qwik 2 binds custom-element JSX props as lower-cased
  attributes only — which is why the SDK wrapper exists rather than using the
  web components directly.
