Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# CHANGELOG

## x.x.x - x-x-x
* [Added] Added appearance.applyLiquidGlass. When set to `true`, changes the values of various properties on the Appearance object used by PaymentSheet, PaymentSheet.FlowController, EmbeddedPaymentElement, CustomerSheet, and AddressViewController to match Liquid Glass when building with Xcode 26 or later and running on iOS 26. This includes appearance.cornerRadius, appearance.borderWidth, appearance.navigationBarStyle, colors.background, navigationBarStyle, and others. This feature is in public preview while we gather feedback and is subject to change. Please use https://github.com/stripe/stripe-ios/issues to file feedback!

| <img src="https://github.com/user-attachments/assets/0d9d333e-41e0-43d0-816b-675916d19d0b" /> | <img src="https://github.com/user-attachments/assets/d043fe5d-65de-4901-be08-21945a2657c1" /> |
| ------ | ------ |

* [Added] appearance.navigationBarStyle. Setting to NavigationBarStyle.Glass will change the sheet navigation bar to a glassy appearance when building with Xcode 26 or later and running on iOS 26. Setting appearance.applyLiquidGlass to `true` will set this value to NavigationBarStyle.Glass.
## 0.54.1 - 2025-10-01

**Fixes**
Expand Down
23 changes: 21 additions & 2 deletions example/src/screens/PaymentSheetAppearance.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
import { Platform } from 'react-native';
import type { PaymentSheet } from '@stripe/stripe-react-native';
import {
NavigationBarStyle,
type PaymentSheet,
} from '@stripe/stripe-react-native';

const appearance: PaymentSheet.AppearanceParams = {
const appearance: PaymentSheet.AppearanceParams = {};

const liquidGlassAppearance: PaymentSheet.AppearanceParams = {
applyLiquidGlass: true,
};

const liquidGlassNavigationOnlyAppearance: PaymentSheet.AppearanceParams = {
navigationBarStyle: NavigationBarStyle.Glass,
};

const customAppearance: PaymentSheet.AppearanceParams = {
font: {
scale: 1.1,
family: Platform.OS === 'android' ? 'macondoregular' : 'Macondo-Regular',
Expand Down Expand Up @@ -64,3 +77,9 @@ const appearance: PaymentSheet.AppearanceParams = {
};

export default appearance;
export {
appearance,
liquidGlassAppearance,
liquidGlassNavigationOnlyAppearance,
customAppearance,
};
86 changes: 82 additions & 4 deletions example/src/screens/PaymentsUICompleteScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,29 @@ import CustomerSessionSwitch from '../components/CustomerSessionSwitch';
import PaymentScreen from '../components/PaymentScreen';
import { API_URL } from '../Config';
import { getClientSecretParams } from '../helpers';
import appearance from './PaymentSheetAppearance';
import {
appearance,
liquidGlassAppearance,
liquidGlassNavigationOnlyAppearance,
customAppearance,
} from './PaymentSheetAppearance';
import { Platform, View, Text, TouchableOpacity } from 'react-native';

enum AppearanceSettings {
default = `default`,
glass = 'glass',
glassNavigation = 'glassNavigation',
custom = 'custom',
}

export default function PaymentsUICompleteScreen() {
const { initPaymentSheet, presentPaymentSheet, resetPaymentSheetCustomer } =
useStripe();
const [paymentSheetEnabled, setPaymentSheetEnabled] = useState(false);
const [loading, setLoading] = useState(false);
const [addressSheetVisible, setAddressSheetVisible] = useState(false);
const [appearanceSettings, setAppearanceSettings] =
useState<AppearanceSettings>(AppearanceSettings.default);
const [clientSecret, setClientSecret] = useState<string>();

const [customerKeyType, setCustomerKeyType] = useState<string>(
Expand Down Expand Up @@ -63,7 +78,6 @@ export default function PaymentsUICompleteScreen() {
};
}
};

const openPaymentSheet = async () => {
if (!clientSecret) {
return;
Expand Down Expand Up @@ -99,6 +113,19 @@ export default function PaymentsUICompleteScreen() {
setLoading(false);
};

const getAppearanceForSetting = (setting: AppearanceSettings) => {
switch (setting) {
case AppearanceSettings.default:
return appearance;
case AppearanceSettings.glass:
return liquidGlassAppearance;
case AppearanceSettings.glassNavigation:
return liquidGlassNavigationOnlyAppearance;
case AppearanceSettings.custom:
return customAppearance;
}
};

const initialisePaymentSheet = useCallback(
async (shippingDetails?: AddressDetails) => {
const { paymentIntent, customer, ...remainingParams } =
Expand Down Expand Up @@ -139,7 +166,7 @@ export default function PaymentsUICompleteScreen() {
defaultBillingDetails: billingDetails,
defaultShippingDetails: shippingDetails,
allowsDelayedPaymentMethods: true,
appearance,
appearance: getAppearanceForSetting(appearanceSettings),
primaryButtonLabel: 'purchase!',
paymentMethodLayout: PaymentMethodLayout.Automatic,
removeSavedPaymentMethodMessage: 'remove this payment method?',
Expand Down Expand Up @@ -206,7 +233,7 @@ export default function PaymentsUICompleteScreen() {
);
}
},
[customerKeyType, initPaymentSheet]
[customerKeyType, appearanceSettings, initPaymentSheet]
);

const toggleCustomerKeyType = (value: boolean) => {
Expand Down Expand Up @@ -242,6 +269,57 @@ export default function PaymentsUICompleteScreen() {
onValueChange={toggleCustomerKeyType}
value={customerKeyType === 'customer_session'}
/>
{Platform.OS === 'ios' && (
<View style={{ marginVertical: 10 }}>
<Text style={{ marginBottom: 8, fontWeight: '500', marginLeft: 10 }}>
Appearance Style
</Text>
<View
style={{
flexDirection: 'row',
borderRadius: 8,
borderWidth: 1,
borderColor: '#ccc',
overflow: 'hidden',
}}
>
{[
{ title: 'Default', value: AppearanceSettings.default },
{ title: 'Glass', value: AppearanceSettings.glass },
{ title: 'Glass Nav', value: AppearanceSettings.glassNavigation },
{ title: 'Custom', value: AppearanceSettings.custom },
].map((option) => (
<TouchableOpacity
key={option.title}
style={{
flex: 1,
padding: 10,
backgroundColor:
appearanceSettings === option.value
? '#007AFF'
: 'transparent',
alignItems: 'center',
}}
onPress={() => {
setAppearanceSettings(option.value);
}}
>
<Text
style={{
fontSize: 13,
color:
appearanceSettings === option.value ? 'white' : '#333',
fontWeight:
appearanceSettings === option.value ? '600' : 'normal',
}}
>
{option.title}
</Text>
</TouchableOpacity>
))}
</View>
</View>
)}
<Button
variant="primary"
loading={loading}
Expand Down
15 changes: 15 additions & 0 deletions ios/PaymentSheetAppearance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ internal class PaymentSheetAppearance {
var appearance = PaymentSheet.Appearance()
guard let userParams = userParams else { return appearance }

if #available(iOS 26.0, *), let applyLiquidGlass = userParams[PaymentSheetAppearanceKeys.APPLY_LIQUID_GLASS] as? Bool,
applyLiquidGlass {
appearance.applyLiquidGlass()
}

if #available(iOS 26.0, *), let navigationBarStyle = userParams[PaymentSheetAppearanceKeys.NAVIGATION_BAR_STYLE] as? String {
if navigationBarStyle == "plain" {
appearance.navigationBarStyle = .plain
} else if navigationBarStyle == "glass" {
appearance.navigationBarStyle = .glass
}
}

if let fontParams = userParams[PaymentSheetAppearanceKeys.FONT] as? NSDictionary {
appearance.font = try buildFont(params: fontParams)
}
Expand Down Expand Up @@ -426,6 +439,8 @@ private struct PaymentSheetAppearanceKeys {
static let BLUR_RADIUS = "blurRadius"
static let X = "x"
static let Y = "y"
static let APPLY_LIQUID_GLASS = "applyLiquidGlass"
static let NAVIGATION_BAR_STYLE = "navigationBarStyle"

static let PRIMARY_BUTTON = "primaryButton"
static let TEXT = "text"
Expand Down
29 changes: 27 additions & 2 deletions src/types/PaymentSheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,12 @@ export type AppearanceParams = RecursivePartial<{
| { light: GlobalColorConfig; dark: GlobalColorConfig };
/** Describes the appearance of shapes in the PaymentSheet, such as buttons, inputs, and tabs. */
shapes: {
/** The border radius used for buttons, inputs, and tabs in your PaymentSheet.
* @default 6.0
/** The border radius used for buttons, inputs, tabs in PaymentSheet
* - Note: On iOS, the behavior of this property is consistent with the behavior of corner radius on `CALayer`
* - Note: On iOS, When `nil`, the behavior depends:
* - iOS 26+ and `UIDesignRequiresCompatibility = NO`: Various `UICornerConfiguration` values are used to match Liquid Glass.
* - Pre-iOS 26: A 6.0 corner radius is applied.
* - Note: On Android, a 6.0 corner radius is applied.
*/
borderRadius: number;
/** The border width used for inputs and tabs in your PaymentSheet.
Expand All @@ -211,8 +215,29 @@ export type AppearanceParams = RecursivePartial<{

/** Describes the inset values applied to Mobile Payment Element forms */
formInsetValues: EdgeInsetsConfig;

/** Setting this boolean to `true` will call the iOS applyLiquidGlass() method
* (https://stripe.dev/stripe-ios/stripepaymentsheet/documentation/stripepaymentsheet/paymentsheet/appearance/applyliquidglass())
* on the Appearance object prior to applying other appearance customizations set on AppearanceParams.
* Requires iOS26 and Xcode 26, and will be ignored if these requirements are not met.
* @default false
*/
applyLiquidGlass?: boolean;

/** Describes the navigation bar style (iOS only)
* @default Plain
*/
navigationBarStyle?: NavigationBarStyle;
}>;

/** Display styles for the navigation bar (iOS only) */
export enum NavigationBarStyle {
/** Default style */
Plain = 'plain',
/** Style to match iOS 26 Liquid Glass. Requires: iOS26 and Xcode 26, and will be ignored if these requirements are not met. */
Glass = 'glass',
}

export type FontConfig = {
/**
* The font used for regular text. PaymentSheet will attempt to use medium and bold versions of this font if they exist.
Expand Down