Skip to content

Commit

Permalink
feat(vue,nuxt): Add function to dynamically update Clerk options (#5235)
Browse files Browse the repository at this point in the history
  • Loading branch information
wobsoriano authored Feb 26, 2025
1 parent 382c302 commit 54a3b5b
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 5 deletions.
35 changes: 35 additions & 0 deletions .changeset/fast-bananas-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
"@clerk/nuxt": minor
"@clerk/vue": minor
---

Introduce `updateClerkOptions()` utility function to update Clerk options on the fly.

Usage:

```vue
<script setup>
import { updateClerkOptions } from '@clerk/vue'
import { dark } from '@clerk/themes'
import { frFR } from '@clerk/localizations'
function enableDarkTheme() {
updateClerkOptions({
appearance: {
baseTheme: dark
}
})
}
function changeToFrench() {
updateClerkOptions({
localization: frFR
})
}
</script>
<template>
<button @click="enableDarkTheme">Enable Dark Theme</button>
<button @click="changeToFrench">Change to French</button>
</template>
```
3 changes: 2 additions & 1 deletion integration/presets/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const vite = applicationConfig()
.addScript('dev', 'pnpm dev')
.addScript('build', 'pnpm build')
.addScript('serve', 'pnpm preview')
.addDependency('@clerk/vue', linkPackage('vue'));
.addDependency('@clerk/vue', linkPackage('vue'))
.addDependency('@clerk/localizations', linkPackage('localizations'));

export const vue = {
vite,
Expand Down
2 changes: 2 additions & 0 deletions integration/templates/vue-vite/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { SignedIn, SignedOut, ClerkLoaded, ClerkLoading } from '@clerk/vue';
import CustomUserButton from './components/CustomUserButton.vue';
import LanguagePicker from './components/LanguagePicker.vue';
</script>

<template>
Expand All @@ -9,6 +10,7 @@ import CustomUserButton from './components/CustomUserButton.vue';
<div style="flex-grow: 1">
<p class="title">Vue Clerk Integration test</p>
</div>
<LanguagePicker />
<SignedIn>
<CustomUserButton />
</SignedIn>
Expand Down
25 changes: 25 additions & 0 deletions integration/templates/vue-vite/src/components/LanguagePicker.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script setup lang="ts">
import { updateClerkOptions } from '@clerk/vue';
const onChange = async (event: Event) => {
const value = (event.target as HTMLSelectElement).value;
let localization: Record<string, any> | undefined;
if (value === 'fr') {
localization = (await import('@clerk/localizations/fr-FR')).frFR;
} else {
localization = undefined;
}
updateClerkOptions({
localization,
});
};
</script>

<template>
<select @change="onChange">
<option value="en">English</option>
<option value="fr">French</option>
</select>
</template>
19 changes: 19 additions & 0 deletions integration/tests/vue/components.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,4 +297,23 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('basic te

await fakeAdmin.deleteIfExists();
});

test('Update Clerk options on the fly with updateClerkOptions()', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });

// Navigate and wait for sign-in component to load
await u.page.goToRelative('/sign-in');
await u.po.signIn.waitForMounted();

// Verify initial English state
await expect(u.page.getByText('Welcome back! Please sign in to continue')).toBeVisible();

// Change to French and verify
await u.page.locator('select').selectOption({ label: 'French' });
await expect(u.page.getByText('pour continuer vers')).toBeVisible();

// Revert to English and verify
await u.page.locator('select').selectOption({ label: 'English' });
await expect(u.page.getByText('Welcome back! Please sign in to continue')).toBeVisible();
});
});
14 changes: 10 additions & 4 deletions packages/nuxt/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,16 @@ export default defineNuxtModule<ModuleOptions>({

// Add auto-imports for Clerk components, composables and client utils
addImportsDir(resolver.resolve('./runtime/composables'));
addImports({
name: 'createRouteMatcher',
from: resolver.resolve('./runtime/client'),
});
addImports([
{
name: 'createRouteMatcher',
from: resolver.resolve('./runtime/client'),
},
{
name: 'updateClerkOptions',
from: resolver.resolve('./runtime/client'),
},
]);

// eslint-disable-next-line @typescript-eslint/consistent-type-imports
const components: Array<keyof typeof import('@clerk/vue')> = [
Expand Down
1 change: 1 addition & 0 deletions packages/nuxt/src/runtime/client/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { createRouteMatcher } from './routeMatcher';
export { updateClerkOptions } from '@clerk/vue';
1 change: 1 addition & 0 deletions packages/vue/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './components';
export * from './composables';

export { clerkPlugin } from './plugin';
export { updateClerkOptions } from './utils';

setErrorThrowerOptions({ packageName: PACKAGE_NAME });
setClerkJsLoadingErrorPackageName(PACKAGE_NAME);
1 change: 1 addition & 0 deletions packages/vue/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './childrenUtils';
export * from './toComputedRefs';
export * from './useClerkLoaded';
export * from './updateClerkOptions';
31 changes: 31 additions & 0 deletions packages/vue/src/utils/updateClerkOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { ClerkOptions } from '@clerk/types';

type ClerkUpdateOptions = Pick<ClerkOptions, 'appearance' | 'localization'>;

/**
* Updates Clerk's options at runtime.
*
* @param options - The Clerk options to update
*
* @example
* import { frFR } from '@clerk/localizations';
* import { dark } from '@clerk/themes';
*
* updateClerkOptions({
* appearance: { baseTheme: dark },
* localization: frFR
* });
*/
export function updateClerkOptions(options: ClerkUpdateOptions) {
if (!window.Clerk) {
throw new Error('Missing Clerk instance');
}

// @ts-expect-error - `__unstable__updateProps` is not exposed as public API from `@clerk/types`
void window.Clerk.__unstable__updateProps({
options: {
localization: options.localization,
},
appearance: options.appearance,
});
}

0 comments on commit 54a3b5b

Please sign in to comment.