Building with the SDK
Any framework
The drop-in components for Svelte, Angular, Astro, server-rendered pages and plain HTML, with or without a build step.
@zevauth/ui puts the ZevAuth components on any page, whatever renders the
rest of it. It works the same in Svelte, Angular, Solid, a Rails or Django
view, or an HTML file with a script tag.
If your app is React, use @zevauth/react instead. If it’s
Vue or Nuxt, use @zevauth/vue, which wraps this package.
Install
npm install @zevauth/ui
import { createZevAuthUI } from '@zevauth/ui';
export const zevauth = createZevAuthUI({
publishableKey: import.meta.env.VITE_ZEVAUTH_KEY,
});
Create the instance once, in a module of its own, and import it wherever you mount a component. Every component from one instance shares one client and one session.
Without a build step
<div id="sign-in"></div>
<script src="https://cdn.jsdelivr.net/npm/@zevauth/ui@0.1.0/dist/zevauth-ui.global.js"></script>
<script>
ZevAuthUI.init({ publishableKey: 'pk_live_...' });
ZevAuthUI.mountSignIn('#sign-in', { afterSignInUrl: '/dashboard' });
</script>
init creates one page-wide instance; calling it again returns the same
one. The mount… functions on ZevAuthUI use it. Pin the version in the
URL so a release you haven’t tested never reaches your users.
Mounting
Every mount… function takes an element, or a selector for one, and the
same props as the React component of the same name:
| Function | Component |
|---|---|
mountSignIn | <SignIn> |
mountSignUp | <SignUp> |
mountSignInWithZevId | <SignInWithZevId> |
mountUserButton | <UserButton> |
mountUserProfile | <UserProfile> |
mountOrganizationSwitcher | <OrganizationSwitcher> |
mountOrganizationProfile | <OrganizationProfile> |
Each returns a handle:
const signIn = zevauth.mountSignIn('#sign-in', { afterSignInUrl: '/dashboard' });
signIn.update({ afterSignInUrl: '/welcome' }); // merged over the current props
signIn.unmount(); // the element is left empty
Mounting into an element that already holds a component replaces it. That makes it safe to call from code that runs more than once, such as a framework’s mount hook in development.
The element has to be in the page when you mount. A selector that matches nothing throws an error naming the selector.
Props that take React elements, such as aside and children on
<SignIn>, are React-only. Everything else, including appearance and
localization, passes straight through.
Appearance and wording
Give them to the instance and every component follows. Give them to one mount and only that component changes:
const zevauth = createZevAuthUI({
publishableKey: 'pk_live_...',
appearance: { theme: 'soft', variables: { colorPrimary: '#4f46e5' } },
localization: { signIn: { submit: 'Se connecter' } },
});
zevauth.mountUserButton('#account', { appearance: { elements: { avatar: 'ring-2' } } });
// Later, for everything already on the page, e.g. after a theme switch:
zevauth.update({ appearance: { colorScheme: 'dark' } });
The options are the same as in React. See Customising the components.
Calling your own API
zevauth.client is the client every component uses. Ask it for a token and
send it as a bearer token:
const token = await zevauth.client.getToken();
await fetch('/api/projects', {
headers: { Authorization: `Bearer ${token}` },
});
With the script-tag build, ZevAuthUI.getClient() returns the page-wide
client. Verify the token on your server with
@zevauth/backend/verify.
Listen for sign-in and sign-out to update the rest of your page:
zevauth.client.addListener(({ isLoaded, user }) => {
document.body.dataset.signedIn = String(isLoaded && user !== null);
});
In your framework
Mount when the framework has put the element in the page, and unmount when it takes it away.
Svelte
<script>
import { onMount } from 'svelte';
import { zevauth } from '$lib/zevauth';
let element;
onMount(() => {
const signIn = zevauth.mountSignIn(element, { afterSignInUrl: '/dashboard' });
return () => signIn.unmount();
});
</script>
<div bind:this={element}></div>
Angular
@Component({ selector: 'app-sign-in', template: '<div #host></div>' })
export class SignInComponent implements AfterViewInit, OnDestroy {
@ViewChild('host') host!: ElementRef<HTMLElement>;
private mounted?: { unmount(): void };
ngAfterViewInit() {
this.mounted = zevauth.mountSignIn(this.host.nativeElement);
}
ngOnDestroy() {
this.mounted?.unmount();
}
}
Astro: see Astro.
What’s inside
The components are built with React, and this package carries its own private copy. Your page doesn’t need React installed, and a React version elsewhere on the page can’t conflict with it.
That copy is most of the package’s size: about 87 kB gzipped. A React app
already has React, which is why it should use @zevauth/react and
download none of this twice.
| Export | What it is |
|---|---|
createZevAuthUI(options) | An instance: client, the mount… functions, update, destroy |
init(options) | The page-wide instance, created once |
mountSignIn … mountOrganizationProfile | Mount with the page-wide instance |
getClient() | The page-wide instance’s client |
createZevAuth, ZevAuthError | The client factory and error class, from the same copy the components use |
options takes publishableKey (or a client), apiUrl, appearance and
localization.
Updated at, Sunday, November 1, 2026