| name | ngx-clerk-migrate-v0-v1 |
| description | Migrate an Angular application from ngx-clerk v0.x (Clerk Core 2, RxJS observables) to v1 (Clerk Core 3, signals, provideClerk) — dependency updates, API renames, and verification. |
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 20 or higher. If it's on Angular 17, 18, or 19, upgrade Angular first.
- Node.js 20.19+.
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:
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.ts with provideClerk() and other providers.
- Change
main.ts from platformBrowserDynamic().bootstrapModule(AppModule) to bootstrapApplication(AppComponent, appConfig).
- Make
AppComponent standalone with standalone: true and move its template dependencies to the imports array.
- Delete
AppModule.
After moving to provideClerk(), remove the ClerkService import and constructor injection from AppComponent -- Clerk now initializes automatically at app startup.
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:
<div *ngIf="clerk.user$ | async as user">{{ user.firstName }}</div>
@if (clerk.user(); as user) {
<p>{{ user.firstName }}</p>
}
Component class migration:
constructor(public clerk: ClerkService) {}
clerk = inject(ClerkService);
Imperative access migration:
this.clerk.user$.pipe(take(1)).subscribe(user => { ... });
const user = this.clerk.user();
effect(() => {
const user = this.clerk.user();
});
New derived signals (no v0.x equivalent):
clerk.isLoaded() -- boolean, true when Clerk has finished initializing
clerk.isSignedIn() -- boolean, true when user is signed in
clerk.userId() -- string | null
clerk.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:
import { ClerkAuthGuardService } from 'ngx-clerk';
canActivate: [ClerkAuthGuardService]
import { canActivateClerk } from 'ngx-clerk';
canActivate: [canActivateClerk]
6. Update type imports
If the app imports types directly from @clerk/types, update them:
import type { UserResource } from '@clerk/types';
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 build and fix any type errors.
- Run
ng serve and 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() from AppComponent after adding provideClerk() -- this causes double initialization.
- Using
clerk.user$ syntax with signals -- signals are called as clerk.user(), not clerk.user$.
- Not converting to standalone bootstrap --
provideClerk() returns EnvironmentProviders which requires bootstrapApplication(), not NgModule.
- Using
afterSignInUrl -- renamed to signInFallbackRedirectUrl in Core 3.
- Importing from
@clerk/types -- package no longer exists, use ngx-clerk or @clerk/shared/types.
Full documentation
https://anagstef.github.io/ngx-clerk/llms-full.txt