- 
                Notifications
    You must be signed in to change notification settings 
- Fork 403
feat(clerk-js,themes,shared): Add theme-usage telemetry #6529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
          
     Merged
      
      
    
  
     Merged
                    Changes from all commits
      Commits
    
    
            Show all changes
          
          
            22 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      10b0d07
              
                feat(clerk-js,themes,shared): Add theme-usage telemetry
              
              
                alexcarpenter 8eeb5eb
              
                add to clerk
              
              
                alexcarpenter a3416ad
              
                refactor
              
              
                alexcarpenter 2a6d113
              
                Merge branch 'main' into alexcarpenter/theme-usage-telemetry
              
              
                alexcarpenter 6733b47
              
                just use name
              
              
                alexcarpenter 065197a
              
                Merge branch 'alexcarpenter/theme-usage-telemetry' of github.com:cler…
              
              
                alexcarpenter e11a15a
              
                Update bundlewatch.config.json
              
              
                alexcarpenter 1655a76
              
                Update packages/shared/src/telemetry/events/theme-usage.ts
              
              
                alexcarpenter 6770c31
              
                Merge branch 'main' into alexcarpenter/theme-usage-telemetry
              
              
                alexcarpenter 6adad53
              
                use consts
              
              
                alexcarpenter 5478a66
              
                add changeset
              
              
                alexcarpenter 6eac2d6
              
                dedupe
              
              
                alexcarpenter 3400e8f
              
                Merge branch 'main' into alexcarpenter/theme-usage-telemetry
              
              
                alexcarpenter dff1950
              
                Merge branch 'main' into alexcarpenter/theme-usage-telemetry
              
              
                alexcarpenter 2a5782a
              
                remove useEffect usage
              
              
                alexcarpenter df24963
              
                Update .changeset/orange-tips-turn.md
              
              
                alexcarpenter 2b11e9e
              
                Update bundlewatch.config.json
              
              
                alexcarpenter 847ca6a
              
                Merge branch 'alexcarpenter/theme-usage-telemetry' of github.com:cler…
              
              
                alexcarpenter 48b474e
              
                Merge branch 'main' into alexcarpenter/theme-usage-telemetry
              
              
                alexcarpenter 48e4dbd
              
                Merge branch 'main' into alexcarpenter/theme-usage-telemetry
              
              
                alexcarpenter 8c6cfc2
              
                Update bundlewatch.config.json
              
              
                alexcarpenter b73ab43
              
                Merge branch 'main' into alexcarpenter/theme-usage-telemetry
              
              
                alexcarpenter File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| '@clerk/clerk-js': patch | ||
| '@clerk/shared': patch | ||
| '@clerk/themes': patch | ||
| --- | ||
|  | ||
| Add theme-usage telemetry | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
        
          
          
            127 changes: 127 additions & 0 deletions
          
          127 
        
  packages/shared/src/telemetry/events/__tests__/theme-usage.test.ts
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| import { EVENT_SAMPLING_RATE, EVENT_THEME_USAGE, eventThemeUsage } from '../theme-usage'; | ||
|  | ||
| describe('eventThemeUsage', () => { | ||
| it('should create telemetry event with shadcn theme name', () => { | ||
| const appearance = { | ||
| theme: { | ||
| __type: 'prebuilt_appearance' as const, | ||
| name: 'shadcn', | ||
| variables: { colorPrimary: 'var(--primary)' }, | ||
| }, | ||
| }; | ||
|  | ||
| const result = eventThemeUsage(appearance); | ||
|  | ||
| expect(result).toEqual({ | ||
| event: EVENT_THEME_USAGE, | ||
| eventSamplingRate: EVENT_SAMPLING_RATE, | ||
| payload: { themeName: 'shadcn' }, | ||
| }); | ||
| }); | ||
|  | ||
| it('should handle string themes', () => { | ||
| const appearance = { | ||
| theme: 'clerk' as any, // String themes are valid at runtime | ||
| }; | ||
|  | ||
| const result = eventThemeUsage(appearance); | ||
|  | ||
| expect(result).toEqual({ | ||
| event: EVENT_THEME_USAGE, | ||
| eventSamplingRate: EVENT_SAMPLING_RATE, | ||
| payload: { themeName: 'clerk' }, | ||
| }); | ||
| }); | ||
|  | ||
| it('should handle array of themes', () => { | ||
| const appearance = { | ||
| theme: [ | ||
| 'clerk' as any, // String themes are valid at runtime | ||
| { | ||
| __type: 'prebuilt_appearance' as const, | ||
| name: 'shadcn', | ||
| }, | ||
| ] as any, | ||
| }; | ||
|  | ||
| const result = eventThemeUsage(appearance); | ||
|  | ||
| expect(result).toEqual({ | ||
| event: EVENT_THEME_USAGE, | ||
| eventSamplingRate: EVENT_SAMPLING_RATE, | ||
| payload: { themeName: 'clerk' }, | ||
| }); | ||
| }); | ||
|  | ||
| it('should handle themes without explicit names', () => { | ||
| const appearance = { | ||
| theme: { | ||
| __type: 'prebuilt_appearance' as const, | ||
| variables: { colorPrimary: 'blue' }, | ||
| }, | ||
| }; | ||
|  | ||
| const result = eventThemeUsage(appearance); | ||
|  | ||
| expect(result).toEqual({ | ||
| event: EVENT_THEME_USAGE, | ||
| eventSamplingRate: EVENT_SAMPLING_RATE, | ||
| payload: { themeName: undefined }, | ||
| }); | ||
| }); | ||
|  | ||
| it('should prioritize theme over deprecated baseTheme', () => { | ||
| const appearance = { | ||
| theme: 'clerk' as any, // String themes are valid at runtime | ||
| baseTheme: { | ||
| __type: 'prebuilt_appearance' as const, | ||
| name: 'shadcn', | ||
| }, | ||
| }; | ||
|  | ||
| const result = eventThemeUsage(appearance); | ||
|  | ||
| expect(result).toEqual({ | ||
| event: EVENT_THEME_USAGE, | ||
| eventSamplingRate: EVENT_SAMPLING_RATE, | ||
| payload: { themeName: 'clerk' }, | ||
| }); | ||
| }); | ||
|  | ||
| it('should use baseTheme when theme is not provided', () => { | ||
| const appearance = { | ||
| baseTheme: { | ||
| __type: 'prebuilt_appearance' as const, | ||
| name: 'shadcn', | ||
| }, | ||
| }; | ||
|  | ||
| const result = eventThemeUsage(appearance); | ||
|  | ||
| expect(result).toEqual({ | ||
| event: EVENT_THEME_USAGE, | ||
| eventSamplingRate: EVENT_SAMPLING_RATE, | ||
| payload: { themeName: 'shadcn' }, | ||
| }); | ||
| }); | ||
|  | ||
| it('should handle undefined appearance', () => { | ||
| const result = eventThemeUsage(); | ||
|  | ||
| expect(result).toEqual({ | ||
| event: EVENT_THEME_USAGE, | ||
| eventSamplingRate: EVENT_SAMPLING_RATE, | ||
| payload: {}, | ||
| }); | ||
| }); | ||
|  | ||
| it('should handle null appearance', () => { | ||
| const result = eventThemeUsage(null as any); | ||
|  | ||
| expect(result).toEqual({ | ||
| event: EVENT_THEME_USAGE, | ||
| eventSamplingRate: EVENT_SAMPLING_RATE, | ||
| payload: {}, | ||
| }); | ||
| }); | ||
| }); | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| export * from './component-mounted'; | ||
| export * from './method-called'; | ||
| export * from './framework-metadata'; | ||
| export * from './theme-usage'; | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| import type { Appearance, BaseTheme, TelemetryEventRaw } from '@clerk/types'; | ||
|  | ||
| export const EVENT_THEME_USAGE = 'THEME_USAGE'; | ||
| export const EVENT_SAMPLING_RATE = 1; | ||
|  | ||
| type EventThemeUsage = { | ||
| /** | ||
| * The name of the theme being used (e.g., "shadcn", "neobrutalism", etc.). | ||
| */ | ||
| themeName?: string; | ||
| }; | ||
|  | ||
| /** | ||
| * Helper function for `telemetry.record()`. Create a consistent event object for tracking theme usage in ClerkProvider. | ||
| * | ||
| * @param appearance - The appearance prop from ClerkProvider. | ||
| * @example | ||
| * telemetry.record(eventThemeUsage(appearance)); | ||
| */ | ||
| export function eventThemeUsage(appearance?: Appearance): TelemetryEventRaw<EventThemeUsage> { | ||
| const payload = analyzeThemeUsage(appearance); | ||
|  | ||
| return { | ||
| event: EVENT_THEME_USAGE, | ||
| eventSamplingRate: EVENT_SAMPLING_RATE, | ||
| payload, | ||
| }; | ||
| } | ||
|  | ||
| /** | ||
| * Analyzes the appearance prop to extract theme usage information for telemetry. | ||
| * | ||
| * @internal | ||
| */ | ||
| function analyzeThemeUsage(appearance?: Appearance): EventThemeUsage { | ||
| if (!appearance || typeof appearance !== 'object') { | ||
| return {}; | ||
| } | ||
|  | ||
| // Prioritize the new theme property over deprecated baseTheme | ||
| const themeProperty = appearance.theme || appearance.baseTheme; | ||
|  | ||
| if (!themeProperty) { | ||
| return {}; | ||
| } | ||
|  | ||
| let themeName: string | undefined; | ||
|  | ||
| if (Array.isArray(themeProperty)) { | ||
| // Look for the first identifiable theme name in the array | ||
| for (const theme of themeProperty) { | ||
| const name = extractThemeName(theme); | ||
| if (name) { | ||
| themeName = name; | ||
| break; | ||
| } | ||
| } | ||
| } else { | ||
| themeName = extractThemeName(themeProperty); | ||
| } | ||
|  | ||
| return { themeName }; | ||
| } | ||
|  | ||
| /** | ||
| * Extracts the theme name from a theme object. | ||
| * | ||
| * @internal | ||
| */ | ||
| function extractThemeName(theme: BaseTheme): string | undefined { | ||
| if (typeof theme === 'string') { | ||
| return theme; | ||
| } | ||
|  | ||
| if (typeof theme === 'object' && theme !== null) { | ||
| // Check for explicit theme name | ||
| if ('name' in theme && typeof theme.name === 'string') { | ||
| return theme.name; | ||
| } | ||
| } | ||
|  | ||
| return undefined; | ||
| } | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import { experimental_createTheme } from '../createTheme'; | ||
|  | ||
| export const experimental__simple = experimental_createTheme({ | ||
| name: 'simple', | ||
| //@ts-expect-error not public api | ||
| simpleStyles: true, | ||
| }); | 
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
      
      Oops, something went wrong.
      
    
  
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Potential duplicate THEME_USAGE events (core load vs React provider); add simple de-duplication
Clerk.load records theme usage when appearance is provided. The React Provider also records on appearance changes. On initial mount, both can fire for the same theme, producing duplicates. Add a simple guard to skip recording the same theme name twice in a row.
Proposed change:
Add this private field to the class (near other private fields):
Verification suggestion:
🏁 Script executed:
Length of output: 2277
Add de-duplication for consecutive THEME_USAGE events
In
packages/clerk-js/src/core/clerk.ts(around lines 447–451), wrap the existing call totelemetry.record(eventThemeUsage…)with a guard that skips recording if the theme name hasn’t changed since the last emission:Add this private field alongside the other
#-prefixed members of the class:Why?
Clerk.loadand the React<ClerkProvider>will fire aTHEME_USAGEevent on mount with the sameappearance.How to verify:
Clerk.load({ appearance: { /*…*/ } }).<ClerkProvider appearance={/* same */}>.THEME_USAGEis emitted on startup, and a second only after you changeappearance.🤖 Prompt for AI Agents