Skip to content

feat(expo): add native component theming via Expo config plugin#8243

Open
chriscanin wants to merge 3 commits intomainfrom
chris/native-components-theming
Open

feat(expo): add native component theming via Expo config plugin#8243
chriscanin wants to merge 3 commits intomainfrom
chris/native-components-theming

Conversation

@chriscanin
Copy link
Copy Markdown
Member

@chriscanin chriscanin commented Apr 6, 2026

Adds support for customizing native Clerk UI components (sign-in, sign-up, user profile) on both iOS and Android via a JSON theme configuration file referenced in the Expo plugin config:

["@clerk/expo", { "theme": "./clerk-theme.json" }]

The JSON schema supports:

  • colors: 15 semantic color tokens (primary, background, danger, etc.)
  • darkColors: dark mode color overrides (iOS uses @Environment colorScheme, Android uses ClerkTheme.darkColors)
  • fonts: fontFamily string or per-style overrides (iOS only)
  • design: borderRadius

Plugin changes:

  • Reads and validates the JSON at prebuild time
  • iOS: Embeds theme in Info.plist; removes UIUserInterfaceStyle when darkColors is present to enable system dark mode
  • Android: Copies JSON to app assets directory

Native changes:

  • iOS: Parses theme from Info.plist, builds light/dark ClerkTheme objects, applies via .environment(.clerkTheme) with colorScheme switching
  • Android: Parses theme from assets JSON, sets Clerk.customTheme
  • Both: AuthView now uses Clerk.customTheme instead of null

Description

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Adds support for customizing native Clerk UI components (sign-in, sign-up,
user profile) on both iOS and Android via a JSON theme configuration file
referenced in the Expo plugin config:

  ["@clerk/expo", { "theme": "./clerk-theme.json" }]

The JSON schema supports:
- colors: 15 semantic color tokens (primary, background, danger, etc.)
- darkColors: dark mode color overrides (iOS uses @Environment colorScheme,
  Android uses ClerkTheme.darkColors)
- fonts: fontFamily string or per-style overrides (iOS only)
- design: borderRadius

Plugin changes:
- Reads and validates the JSON at prebuild time
- iOS: Embeds theme in Info.plist; removes UIUserInterfaceStyle when
  darkColors is present to enable system dark mode
- Android: Copies JSON to app assets directory

Native changes:
- iOS: Parses theme from Info.plist, builds light/dark ClerkTheme objects,
  applies via .environment(\.clerkTheme) with colorScheme switching
- Android: Parses theme from assets JSON, sets Clerk.customTheme
- Both: AuthView now uses Clerk.customTheme instead of null
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 6, 2026

🦋 Changeset detected

Latest commit: b5af733

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@clerk/expo Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Apr 8, 2026 5:32pm

Request Review

@github-actions github-actions bot added the expo label Apr 6, 2026
…erkDesign signature

Two fixes needed to make the Android theme actually take effect:

1. Call loadThemeFromAssets() AFTER Clerk.initialize() instead of before.
   Clerk.initialize() accepts a `theme` parameter that defaults to null and
   assigns it to Clerk.customTheme on every call, which was wiping out the
   theme we just loaded.

2. Use the real ClerkDesign(borderRadius: Dp) constructor signature. The
   previous code passed nonexistent fontFamily and nullable borderRadius
   parameters that don't compile against clerk-android-ui.
@chriscanin chriscanin marked this pull request as ready for review April 8, 2026 17:18
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 5885e6e1-c4c2-4bf8-9b69-2a83248e3e96

📥 Commits

Reviewing files that changed from the base of the PR and between 221e30c and b5af733.

📒 Files selected for processing (1)
  • .changeset/expo-native-component-theming.md
✅ Files skipped from review due to trivial changes (1)
  • .changeset/expo-native-component-theming.md

📝 Walkthrough

Walkthrough

Adds native theming support across the Expo Clerk integration. A new Expo config plugin (withClerkTheme) validates a JSON theme, embeds it into iOS Info.plist, and copies it to Android assets. Android runtime parsing was added to ClerkExpoModule to load clerk_theme.json into Clerk.customTheme. Two Android Compose/Activity view points (ClerkAuthExpoView, ClerkAuthActivity) now pass Clerk.customTheme into AuthView. iOS ClerkViewFactory reads ClerkTheme from Info.plist, exposes lightTheme/darkTheme, and threads the selected theme into view controllers and SwiftUI views. A changeset and plugin wiring were added.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main feature: adding native component theming support via an Expo config plugin.
Description check ✅ Passed The description is well-detailed and directly related to the changeset, explaining the feature, JSON schema, platform-specific implementation, and testing status.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/expo/app.plugin.js`:
- Line 621: HEX_COLOR_REGEX currently allows 3-character hex colors but iOS
colorFromHex only supports 6- and 8-character forms, causing valid-looking
inputs to return nil; update HEX_COLOR_REGEX to remove the 3-char alternative so
it only matches 6- or 8-digit hex (e.g., change
/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/ to a pattern that omits the
{3} branch), and run/update any tests or callers that assume 3-char acceptance;
alternatively, if you prefer to keep 3-char support, implement expansion logic
in the native colorFromHex parser to expand 3-char shorthand to 6-char before
parsing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 4526911d-c482-4634-a181-3952e94714b2

📥 Commits

Reviewing files that changed from the base of the PR and between 2c06a5f and 221e30c.

📒 Files selected for processing (5)
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthActivity.kt
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkAuthExpoView.kt
  • packages/expo/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt
  • packages/expo/app.plugin.js
  • packages/expo/ios/ClerkViewFactory.swift

'shadow',
];

const HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Validation allows 3-char hex but parsers don't support it.

HEX_COLOR_REGEX accepts 3-character hex colors (e.g., #FFF), but the iOS colorFromHex function only handles 6 and 8 character hex strings—3-char hex falls through to default and returns nil. This causes colors to silently fail to apply despite passing validation.

Either remove 3-char support from the regex or add expansion logic in the native parsers.

Proposed fix: Remove 3-char hex from regex
-const HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
+const HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
const HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/expo/app.plugin.js` at line 621, HEX_COLOR_REGEX currently allows
3-character hex colors but iOS colorFromHex only supports 6- and 8-character
forms, causing valid-looking inputs to return nil; update HEX_COLOR_REGEX to
remove the 3-char alternative so it only matches 6- or 8-digit hex (e.g., change
/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/ to a pattern that omits the
{3} branch), and run/update any tests or callers that assume 3-char acceptance;
alternatively, if you prefer to keep 3-char support, implement expansion logic
in the native colorFromHex parser to expand 3-char shorthand to 6-char before
parsing.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 8, 2026

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@8243

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8243

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8243

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8243

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8243

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8243

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8243

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8243

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8243

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8243

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8243

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8243

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8243

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8243

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8243

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8243

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8243

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8243

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8243

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8243

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8243

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8243

commit: b5af733

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant