TenantProvider gives your app tenant switching: list tenants, switch between them, auto-resolve the default, and track loading state. Wrap your app (inside AuthProvider), use useTenant() to read state, and drop in TenantSwitcher for a ready-made UI — or build your own.
Quick start
1. Wrap your app
TenantProvider must be inside AuthProvider. Pass the SDK methods generated by codegen:
import { AuthProvider , TenantProvider } from 'vertz/ui/auth' ;
import { auth } from './.vertz/generated/auth' ;
function App () {
return (
< AuthProvider >
< TenantProvider
listTenants = { auth . listTenants }
switchTenant = { auth . switchTenant }
onSwitchComplete = { ( tenantId ) => {
// Navigate, refetch data, etc.
navigate ({ to: '/dashboard' });
} }
>
< AppContent />
</ TenantProvider >
</ AuthProvider >
);
}
2. Drop in the switcher
import { TenantSwitcher } from 'vertz/ui-auth' ;
function Sidebar () {
return (
< nav >
< TenantSwitcher />
</ nav >
);
}
3. Or build a custom UI with the hook
import { useTenant } from 'vertz/ui/auth' ;
function CustomTenantPicker () {
const { tenants , currentTenantId , switchTenant , isLoading } = useTenant ();
if ( isLoading ) return < div > Loading tenants... </ div > ;
return (
< div >
{ tenants . map (( tenant ) => (
< button
key = { tenant . id }
onClick = { () => switchTenant ( tenant . id ) }
data-active = { tenant . id === currentTenantId ? 'true' : undefined }
>
{ tenant . name }
</ button >
)) }
</ div >
);
}
TenantProvider
Fetches the tenant list on mount and provides tenant state to all children via context.
< TenantProvider
listTenants = { auth . listTenants }
switchTenant = { auth . switchTenant }
onSwitchComplete = { ( tenantId ) => {
/* ... */
} }
>
< App />
</ TenantProvider >
Prop Type Description listTenants() => Promise<Result<ListTenantsResponse>>SDK method to fetch tenants (generated by codegen) switchTenantSdkMethod<{ tenantId: string }>SDK method to switch tenant (generated by codegen) onSwitchComplete(tenantId: string) => voidCalled after a successful switch — use for navigation childrenunknownApp content
TenantProvider must be rendered inside AuthProvider. It reads from AuthContext internally —
you don’t need to pass auth state manually.
Automatic query invalidation on tenant switch
When switchTenant() succeeds, all active tenant-scoped queries are automatically invalidated. Cached data from the previous tenant is cleared immediately (no stale cross-tenant data visible), then each query refetches with the new tenant context. Non-tenant-scoped queries (e.g., global settings) are left untouched.
This happens automatically — no manual invalidation or onSwitchComplete callback needed for data freshness.
useTenant()
Returns reactive tenant state. All signal properties are auto-unwrapped by the compiler.
const tenant = useTenant ();
Property Type Description tenantsTenantInfo[]All tenants the user belongs to currentTenantIdstring | undefinedTenant in the current session lastTenantIdstring | undefinedLast tenant the user switched to resolvedDefaultIdstring | undefinedServer-recommended default tenant isLoadingbooleantrue while fetching tenantsswitchTenant(tenantId: string) => Promise<Result<void, Error>>Switch to a different tenant
TenantInfo
interface TenantInfo {
id : string ;
name : string ;
[ key : string ] : unknown ; // logo, plan, role — whatever you return from listTenants
}
TenantSwitcher
A ready-made dropdown component that renders the current tenant with a menu to switch. Import from @vertz/ui-auth:
import { TenantSwitcher } from 'vertz/ui-auth' ;
< TenantSwitcher
renderItem = { ( tenant ) => (
< div style = { { display: 'flex' , alignItems: 'center' , gap: '8px' } } >
< img src = { tenant . logo } width = { 20 } height = { 20 } />
< span > { tenant . name } </ span >
</ div >
) }
className = "my-tenant-picker"
/> ;
Prop Type Default Description renderItem(tenant: TenantInfo) => unknownRenders tenant.name Custom render for each tenant item (trigger and dropdown) classNamestring— CSS class for the container
Styling
TenantSwitcher uses data-part attributes for styling:
Part Element Description tenant-switcherContainer div Root wrapper triggerbuttonShows the current tenant contentdivDropdown panel itembuttonIndividual tenant option
Items have data-selected="true" when they match the current tenant.
[ data-part = 'tenant-switcher' ] {
position : relative ;
}
[ data-part = 'trigger' ] {
/* trigger button styles */
}
[ data-part = 'content' ] {
/* dropdown styles */
}
[ data-part = 'item' ] {
/* item styles */
}
[ data-part = 'item' ][ data-selected ] {
/* selected item styles */
}
Auto-resolve on login
When a user logs in, their session has no tenantId. The resolvedDefaultId from useTenant() tells you which tenant the server recommends — typically the last-accessed tenant.
Use this to auto-switch on app mount:
import { useTenant } from 'vertz/ui/auth' ;
import { watch } from 'vertz/ui' ;
function TenantAutoResolver () {
const { resolvedDefaultId , currentTenantId , switchTenant , isLoading } = useTenant ();
watch (
() => isLoading ,
( loading ) => {
if ( ! loading && resolvedDefaultId && ! currentTenantId ) {
switchTenant ( resolvedDefaultId );
}
},
);
return null ;
}
Place this inside TenantProvider. After the tenant list loads, it auto-switches to the resolved default if no tenant is currently selected.
Common patterns
function Header () {
const { tenants , currentTenantId } = useTenant ();
const current = tenants . find (( t ) => t . id === currentTenantId );
return (
< header >
< span > { current ?. name ?? 'No tenant selected' } </ span >
</ header >
);
}
Redirect after switch
< TenantProvider
listTenants = { auth . listTenants }
switchTenant = { auth . switchTenant }
onSwitchComplete = { () => {
// Redirect to dashboard after switching
navigate ({ to: '/dashboard' });
} }
>
With custom avatars per tenant
< TenantSwitcher
renderItem = { ( tenant ) => (
< div style = { { display: 'flex' , alignItems: 'center' , gap: '8px' } } >
< Avatar src = { tenant . logo } fallback = { tenant . name [ 0 ] } size = "sm" />
< span > { tenant . name } </ span >
</ div >
) }
/>
Server setup
The client-side tenant features require server-side configuration. See the server multi-tenancy guide for:
Configuring tenant.verifyMembership, tenant.listTenants, and tenant.resolveDefault
How tenant-scoped JWTs work
Automatic entity tenant scoping
Endpoint reference
Next steps
Server Multi-Tenancy Configure tenant endpoints, membership verification, and auto-resolve.
Authentication AuthProvider, useAuth(), and session management.
Access Control Entitlements and tenant-scoped access rules.