ngx-clerk v0.x to v1.0 Migration Skill
You are migrating an Angular application from ngx-clerk v0.x (Clerk Core 2 / ClerkJS v5) to ngx-clerk v1.0 (Clerk Core 3 / ClerkJS v6).
Prerequisites
- The target app must be on Angular 19 or higher. If it's on Angular 17 or 18, upgrade Angular first.
- Node.js 20.9+.
Step-by-step migration
1. Update dependencies
npm install ngx-clerk@1
Remove @clerk/types from package.json if present -- it's been merged into @clerk/shared/types and is installed automatically.
2. Replace __init() with provideClerk()
Find the ClerkService.__init() call. It's typically in app.component.ts:
// OLD -- delete this
import { ClerkService } from 'ngx-clerk';
constructor(private clerk: ClerkService) {
clerk.__init({ publishableKey: '...', afterSignInUrl: '/dashboard' });
}
Replace with provideClerk() in the app's provider configuration.
If the app uses standalone bootstrap (bootstrapApplication in main.ts), add it to app.config.ts:
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideClerk } from 'ngx-clerk';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideClerk({
publishableKey: 'pk_test_...',
}),
],
};
If the app uses NgModule bootstrap (AppModule), you cannot use provideClerk() directly because it returns EnvironmentProviders. You must first convert the app to standalone bootstrap. The minimal conversion is:
- Create
app.config.tswithprovideClerk()and other providers. - Change
main.tsfromplatformBrowserDynamic().bootstrapModule(AppModule)tobootstrapApplication(AppComponent, appConfig). - Make
AppComponentstandalone withstandalone: trueand move its template dependencies to theimportsarray. - Delete
AppModule.
After moving to provideClerk(), remove the ClerkService import and constructor injection from AppComponent -- Clerk now initializes automatically via APP_INITIALIZER.
3. Rename redirect props
Apply these renames in provideClerk() options, component props, and any redirectToSignIn()/redirectToSignUp() calls:
| Old (v0.x) | New (v1.0) |
|---|---|
afterSignInUrl |
signInFallbackRedirectUrl |
afterSignUpUrl |
signUpFallbackRedirectUrl |
afterSwitchOrganizationUrl |
afterSelectOrganizationUrl |
redirectUrl |
signInFallbackRedirectUrl |
For forced redirects, use signInForceRedirectUrl / signUpForceRedirectUrl.
4. Replace RxJS observables with signals
Search the codebase for these patterns and replace them:
| Find | Replace with |
|---|---|
clerk.clerk$ |
clerk.clerk() |
clerk.user$ |
clerk.user() |
clerk.session$ |
clerk.session() |
clerk.client$ |
clerk.client() |
clerk.organization$ |
clerk.organization() |
.pipe(take(1)).subscribe( |
Direct signal read |
| async |
Direct signal read in template |
Template migration:
<!-- OLD -->
<div *ngIf="clerk.user$ | async as user">{{ user.firstName }}</div>
<!-- NEW -->
@if (clerk.user(); as user) {
<p>{{ user.firstName }}</p>
}
Component class migration:
// OLD
constructor(public clerk: ClerkService) {}
// NEW
clerk = inject(ClerkService);
Imperative access migration:
// OLD
this.clerk.user$.pipe(take(1)).subscribe(user => { ... });
// NEW
const user = this.clerk.user(); // synchronous read
// or use effect() for reactive code:
effect(() => {
const user = this.clerk.user();
// runs whenever user changes
});
New derived signals (no v0.x equivalent):
clerk.isLoaded()--boolean, true when Clerk has finished initializingclerk.isSignedIn()--boolean, true when user is signed inclerk.userId()--string | nullclerk.orgId()--string | null
If the app still needs an Observable, use toObservable():
import { toObservable } from '@angular/core/rxjs-interop';
user$ = toObservable(this.clerk.user);
5. Replace auth guard
Search for ClerkAuthGuardService and replace with canActivateClerk:
// OLD
import { ClerkAuthGuardService } from 'ngx-clerk';
canActivate: [ClerkAuthGuardService]
// NEW
import { canActivateClerk } from 'ngx-clerk';
canActivate: [canActivateClerk]
6. Update type imports
If the app imports types directly from @clerk/types, update them:
// OLD
import type { UserResource } from '@clerk/types';
// NEW
import type { UserResource } from 'ngx-clerk';
Types re-exported from ngx-clerk are unchanged -- they now come from @clerk/shared/types internally but the consumer import path is the same.
7. Update upstream Clerk Core 3 API renames
Search for and replace these deprecated patterns:
| Old | New |
|---|---|
client.activeSessions |
client.sessions |
strategy: 'saml' |
strategy: 'enterprise_sso' |
user.samlAccounts |
user.enterpriseAccounts |
appearance.layout |
appearance.options |
8. Verify
After all changes:
- Run
ng buildand fix any type errors. - Run
ng serveand test:- Sign in / sign up flows work.
- Protected routes redirect to sign-in when not authenticated.
- User data renders correctly from signals.
- Clerk UI components (
<clerk-user-button />, etc.) render.
Files typically modified
| File | Changes |
|---|---|
package.json |
Update ngx-clerk, remove @clerk/types |
app.config.ts (or app.module.ts -> standalone conversion) |
Add provideClerk(), remove ClerkService init |
app.component.ts |
Remove ClerkService injection and __init() call |
app.routes.ts / routing config |
ClerkAuthGuardService -> canActivateClerk |
Any component using clerk.user$ etc. |
RxJS -> signals |
Any template with | async on Clerk data |
Signal reads |
Common mistakes
- Forgetting to remove
__init()fromAppComponentafter addingprovideClerk()-- this causes double initialization. - Using
clerk.user$syntax with signals -- signals are called asclerk.user(), notclerk.user$. - Not converting to standalone bootstrap --
provideClerk()returnsEnvironmentProviderswhich requiresbootstrapApplication(), notNgModule. - Using
afterSignInUrl-- renamed tosignInFallbackRedirectUrlin Core 3. - Importing from
@clerk/types-- package no longer exists, usengx-clerkor@clerk/shared/types.
Source: anagstef/ngx-clerk — distributed by TomeVault.