The API for interacting with a customer's privacy consent. It is similar to the [Customer Privacy API in storefront](/docs/api/customer-privacy).
import {useState} from 'react';
import {
reactExtension,
useCustomerPrivacy,
} from '@shopify/ui-extensions-react/checkout';
// 1. Choose an extension target
export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);
function Extension() {
// 2. Subscribe to customer privacy consent values
const {
visitorConsent: {
analytics,
marketing,
preferences,
saleOfData,
},
} = useCustomerPrivacy();
// 3. Use consent values
console.log('analytics', analytics);
console.log('marketing', marketing);
console.log('preferences', preferences);
console.log('saleOfData', saleOfData);
}
import {
extension,
Text,
} from '@shopify/ui-extensions/checkout';
// 1. Choose an extension target
export default extension(
'purchase.checkout.block.render',
(root, {customerPrivacy}) => {
const {
visitorConsent: {
analytics,
marketing,
preferences,
saleOfData,
},
} = customerPrivacy.current;
// 2. Use consent values
console.log('initialValues');
console.log('analytics', analytics);
console.log('marketing', marketing);
console.log('preferences', preferences);
console.log('saleOfData', saleOfData);
// 3. Update component state when customerPrivacy changes
customerPrivacy.subscribe((value) => {
if (!value) {
return;
}
const {
visitorConsent: {
analytics,
marketing,
preferences,
saleOfData,
},
} = value;
console.log('updatedValues');
console.log('analytics', analytics);
console.log('marketing', marketing);
console.log('preferences', preferences);
console.log('saleOfData', saleOfData);
});
},
);
The base API object provided to `purchase` extension targets.
Allows setting and updating customer privacy consent settings and tracking consent metafields. > Note: Requires the [`customer_privacy` capability](/docs/api/checkout-ui-extensions/unstable/configuration#collect-buyer-consent) to be set to `true`. {% include /apps/checkout/privacy-icon.md %} Requires access to [protected customer data](/docs/apps/store/data-protection/protected-customer-data).
Customer privacy consent settings and a flag denoting if consent has previously been collected.
visitorConsent: VisitorConsentChange
export type ApplyTrackingConsentChangeType = ( visitorConsent: VisitorConsentChange, ) => Promise<TrackingConsentChangeResult>;
Visitor consents to recording data to understand how customers interact with the site.
Visitor consents to ads and marketing communications based on customer interests.
Tracking consent metafield data to be saved. If the value is `null`, the metafield will be deleted.
Visitor consent to remembering customer preferences, such as country or language, to personalize visits to the website.
Opts the visitor out of data sharing / sales.
The name of the metafield. It must be between 3 and 30 characters in length (inclusive).
The information to be stored as metadata. If the value is `null`, the metafield will be deleted.
Visitor consents to recording data to understand how customers interact with the site.
Visitor consents to ads and marketing communications based on customer interests.
Visitor consent to remembering customer preferences, such as country or language, to personalize visits to the website.
Opts the visitor out of data sharing / sales.
TrackingConsentChangeResultSuccess | TrackingConsentChangeResultError
The returned result of a successful tracking consent preference update.
The type of the `TrackingConsentChangeResultSuccess` API.
The returned result of an unsuccessful tracking consent preference update with a message detailing the type of error that occurred.
A message that explains the error. This message is useful for debugging. It is **not** localized, and therefore should not be presented directly to the buyer.
The type of the `TrackingConsentChangeResultError` API.
An object containing flags for each consent property denoting whether they can be processed based on visitor consent, merchant configuration, and user location.
Stored tracking consent metafield data.
Details about the visitor's current location for use in evaluating if more granular consent controls should render.
Whether the visitor is in a region requiring data sale opt-outs.
Whether a consent banner should be displayed by default when the page loads. Use this as the initial open/expanded state of the consent banner. This is determined by the visitor's current privacy consent, the shop's [region visibility configuration](https://help.shopify.com/en/manual/privacy-and-security/privacy/customer-privacy-settings/privacy-settings#add-a-cookie-banner) settings, and the region in which the visitor is located.
An object containing the customer's current privacy consent settings. *
Can collect customer analytics about how the shop was used and interactions made on the shop.
Can collect customer preference for marketing, attribution and targeted advertising from the merchant.
Can collect customer preferences such as language, currency, size, and more.
Can collect customer preference for sharing data with third parties, usually for behavioral advertising.
The name of the metafield. It must be between 3 and 30 characters in length (inclusive).
The information to be stored as metadata.
The [ISO 3166 Alpha-2 format](https://www.iso.org/iso-3166-country-codes.html) for the buyer's country. {% include /apps/checkout/privacy-icon.md %} Requires level 1 access to [protected customer data](/docs/apps/store/data-protection/protected-customer-data).
The buyer's province code, such as state, province, prefecture, or region. Province codes can be found by clicking on the `Subdivisions assigned codes` column for countries listed [here](https://en.wikipedia.org/wiki/ISO_3166-2). {% include /apps/checkout/privacy-icon.md %} Requires level 1 access to [protected customer data](/docs/apps/store/data-protection/protected-customer-data).
'AC' | 'AD' | 'AE' | 'AF' | 'AG' | 'AI' | 'AL' | 'AM' | 'AN' | 'AO' | 'AR' | 'AT' | 'AU' | 'AW' | 'AX' | 'AZ' | 'BA' | 'BB' | 'BD' | 'BE' | 'BF' | 'BG' | 'BH' | 'BI' | 'BJ' | 'BL' | 'BM' | 'BN' | 'BO' | 'BQ' | 'BR' | 'BS' | 'BT' | 'BV' | 'BW' | 'BY' | 'BZ' | 'CA' | 'CC' | 'CD' | 'CF' | 'CG' | 'CH' | 'CI' | 'CK' | 'CL' | 'CM' | 'CN' | 'CO' | 'CR' | 'CU' | 'CV' | 'CW' | 'CX' | 'CY' | 'CZ' | 'DE' | 'DJ' | 'DK' | 'DM' | 'DO' | 'DZ' | 'EC' | 'EE' | 'EG' | 'EH' | 'ER' | 'ES' | 'ET' | 'FI' | 'FJ' | 'FK' | 'FO' | 'FR' | 'GA' | 'GB' | 'GD' | 'GE' | 'GF' | 'GG' | 'GH' | 'GI' | 'GL' | 'GM' | 'GN' | 'GP' | 'GQ' | 'GR' | 'GS' | 'GT' | 'GW' | 'GY' | 'HK' | 'HM' | 'HN' | 'HR' | 'HT' | 'HU' | 'ID' | 'IE' | 'IL' | 'IM' | 'IN' | 'IO' | 'IQ' | 'IR' | 'IS' | 'IT' | 'JE' | 'JM' | 'JO' | 'JP' | 'KE' | 'KG' | 'KH' | 'KI' | 'KM' | 'KN' | 'KP' | 'KR' | 'KW' | 'KY' | 'KZ' | 'LA' | 'LB' | 'LC' | 'LI' | 'LK' | 'LR' | 'LS' | 'LT' | 'LU' | 'LV' | 'LY' | 'MA' | 'MC' | 'MD' | 'ME' | 'MF' | 'MG' | 'MK' | 'ML' | 'MM' | 'MN' | 'MO' | 'MQ' | 'MR' | 'MS' | 'MT' | 'MU' | 'MV' | 'MW' | 'MX' | 'MY' | 'MZ' | 'NA' | 'NC' | 'NE' | 'NF' | 'NG' | 'NI' | 'NL' | 'NO' | 'NP' | 'NR' | 'NU' | 'NZ' | 'OM' | 'PA' | 'PE' | 'PF' | 'PG' | 'PH' | 'PK' | 'PL' | 'PM' | 'PN' | 'PS' | 'PT' | 'PY' | 'QA' | 'RE' | 'RO' | 'RS' | 'RU' | 'RW' | 'SA' | 'SB' | 'SC' | 'SD' | 'SE' | 'SG' | 'SH' | 'SI' | 'SJ' | 'SK' | 'SL' | 'SM' | 'SN' | 'SO' | 'SR' | 'SS' | 'ST' | 'SV' | 'SX' | 'SY' | 'SZ' | 'TA' | 'TC' | 'TD' | 'TF' | 'TG' | 'TH' | 'TJ' | 'TK' | 'TL' | 'TM' | 'TN' | 'TO' | 'TR' | 'TT' | 'TV' | 'TW' | 'TZ' | 'UA' | 'UG' | 'UM' | 'US' | 'UY' | 'UZ' | 'VA' | 'VC' | 'VE' | 'VG' | 'VN' | 'VU' | 'WF' | 'WS' | 'XK' | 'YE' | 'YT' | 'ZA' | 'ZM' | 'ZW' | 'ZZ'
Returns the current customer privacy settings and metadata and re-renders your component if the customer privacy settings change.
Returns the current customer privacy settings and metadata and re-renders your component if the customer privacy settings change.
export function useCustomerPrivacy< Target extends RenderExtensionTarget = RenderExtensionTarget, >(): CustomerPrivacy { return useSubscription(useApi<Target>().customerPrivacy); }
An object containing flags for each consent property denoting whether they can be processed based on visitor consent, merchant configuration, and user location.
Stored tracking consent metafield data.
Details about the visitor's current location for use in evaluating if more granular consent controls should render.
Whether the visitor is in a region requiring data sale opt-outs.
Whether a consent banner should be displayed by default when the page loads. Use this as the initial open/expanded state of the consent banner. This is determined by the visitor's current privacy consent, the shop's [region visibility configuration](https://help.shopify.com/en/manual/privacy-and-security/privacy/customer-privacy-settings/privacy-settings#add-a-cookie-banner) settings, and the region in which the visitor is located.
An object containing the customer's current privacy consent settings. *
Can collect customer analytics about how the shop was used and interactions made on the shop.
Can collect customer preference for marketing, attribution and targeted advertising from the merchant.
Can collect customer preferences such as language, currency, size, and more.
Can collect customer preference for sharing data with third parties, usually for behavioral advertising.
The name of the metafield. It must be between 3 and 30 characters in length (inclusive).
The information to be stored as metadata.
The [ISO 3166 Alpha-2 format](https://www.iso.org/iso-3166-country-codes.html) for the buyer's country. {% include /apps/checkout/privacy-icon.md %} Requires level 1 access to [protected customer data](/docs/apps/store/data-protection/protected-customer-data).
The buyer's province code, such as state, province, prefecture, or region. Province codes can be found by clicking on the `Subdivisions assigned codes` column for countries listed [here](https://en.wikipedia.org/wiki/ISO_3166-2). {% include /apps/checkout/privacy-icon.md %} Requires level 1 access to [protected customer data](/docs/apps/store/data-protection/protected-customer-data).
'AC' | 'AD' | 'AE' | 'AF' | 'AG' | 'AI' | 'AL' | 'AM' | 'AN' | 'AO' | 'AR' | 'AT' | 'AU' | 'AW' | 'AX' | 'AZ' | 'BA' | 'BB' | 'BD' | 'BE' | 'BF' | 'BG' | 'BH' | 'BI' | 'BJ' | 'BL' | 'BM' | 'BN' | 'BO' | 'BQ' | 'BR' | 'BS' | 'BT' | 'BV' | 'BW' | 'BY' | 'BZ' | 'CA' | 'CC' | 'CD' | 'CF' | 'CG' | 'CH' | 'CI' | 'CK' | 'CL' | 'CM' | 'CN' | 'CO' | 'CR' | 'CU' | 'CV' | 'CW' | 'CX' | 'CY' | 'CZ' | 'DE' | 'DJ' | 'DK' | 'DM' | 'DO' | 'DZ' | 'EC' | 'EE' | 'EG' | 'EH' | 'ER' | 'ES' | 'ET' | 'FI' | 'FJ' | 'FK' | 'FO' | 'FR' | 'GA' | 'GB' | 'GD' | 'GE' | 'GF' | 'GG' | 'GH' | 'GI' | 'GL' | 'GM' | 'GN' | 'GP' | 'GQ' | 'GR' | 'GS' | 'GT' | 'GW' | 'GY' | 'HK' | 'HM' | 'HN' | 'HR' | 'HT' | 'HU' | 'ID' | 'IE' | 'IL' | 'IM' | 'IN' | 'IO' | 'IQ' | 'IR' | 'IS' | 'IT' | 'JE' | 'JM' | 'JO' | 'JP' | 'KE' | 'KG' | 'KH' | 'KI' | 'KM' | 'KN' | 'KP' | 'KR' | 'KW' | 'KY' | 'KZ' | 'LA' | 'LB' | 'LC' | 'LI' | 'LK' | 'LR' | 'LS' | 'LT' | 'LU' | 'LV' | 'LY' | 'MA' | 'MC' | 'MD' | 'ME' | 'MF' | 'MG' | 'MK' | 'ML' | 'MM' | 'MN' | 'MO' | 'MQ' | 'MR' | 'MS' | 'MT' | 'MU' | 'MV' | 'MW' | 'MX' | 'MY' | 'MZ' | 'NA' | 'NC' | 'NE' | 'NF' | 'NG' | 'NI' | 'NL' | 'NO' | 'NP' | 'NR' | 'NU' | 'NZ' | 'OM' | 'PA' | 'PE' | 'PF' | 'PG' | 'PH' | 'PK' | 'PL' | 'PM' | 'PN' | 'PS' | 'PT' | 'PY' | 'QA' | 'RE' | 'RO' | 'RS' | 'RU' | 'RW' | 'SA' | 'SB' | 'SC' | 'SD' | 'SE' | 'SG' | 'SH' | 'SI' | 'SJ' | 'SK' | 'SL' | 'SM' | 'SN' | 'SO' | 'SR' | 'SS' | 'ST' | 'SV' | 'SX' | 'SY' | 'SZ' | 'TA' | 'TC' | 'TD' | 'TF' | 'TG' | 'TH' | 'TJ' | 'TK' | 'TL' | 'TM' | 'TN' | 'TO' | 'TR' | 'TT' | 'TV' | 'TW' | 'TZ' | 'UA' | 'UG' | 'UM' | 'US' | 'UY' | 'UZ' | 'VA' | 'VC' | 'VE' | 'VG' | 'VN' | 'VU' | 'WF' | 'WS' | 'XK' | 'YE' | 'YT' | 'ZA' | 'ZM' | 'ZW' | 'ZZ'
Visitor consents to recording data to understand how customers interact with the site.
Visitor consents to ads and marketing communications based on customer interests.
Visitor consent to remembering customer preferences, such as country or language, to personalize visits to the website.
Opts the visitor out of data sharing / sales.
The API for interacting with a customer's privacy consent. It is similar to the [Customer Privacy API in storefront](/docs/api/customer-privacy).
You can apply changes to customer consent by using the `applyTrackingConsentChanges` API. > Note: Requires the [`customer_privacy` capability](/docs/api/checkout-ui-extensions/unstable/configuration#collect-buyer-consent) to be set to `true`.
import {useState} from 'react';
import {
reactExtension,
BlockStack,
Button,
Checkbox,
Form,
Grid,
Link,
Modal,
Sheet,
TextBlock,
useApi,
useCustomerPrivacy,
} from '@shopify/ui-extensions-react/checkout';
import type {VisitorConsent} from '@shopify/ui-extensions/checkout';
export default reactExtension(
'purchase.checkout.footer.render-after',
() => <Extension />,
);
function Extension() {
const {applyTrackingConsentChange, ui} =
useApi();
const {
shouldShowBanner,
visitorConsent: {
analytics,
marketing,
preferences,
saleOfData,
},
} = useCustomerPrivacy();
const [
consentFormValues,
setConsentFormValues,
] = useState({
analytics,
marketing,
preferences,
saleOfData,
});
const sheetId = 'sheet-consent';
const modalId = 'modal-consent';
const getCheckboxOnChangeHandler = (
key: string,
) => {
return function (checked: boolean) {
setConsentFormValues({
...consentFormValues,
[key]: checked,
});
};
};
const handleConsentChange = async (
visitorConsent?: VisitorConsent,
) => {
try {
const result =
await applyTrackingConsentChange({
...(visitorConsent
? visitorConsent
: consentFormValues),
type: 'changeVisitorConsent',
});
// Check if operation was successful
if (result.type === 'success') {
ui.overlay.close(modalId);
ui.overlay.close(sheetId);
} else {
// Handle failure case here
}
} catch (error) {
// Handle error case here
}
};
const consentFormMarkup = (
<Form onSubmit={() => handleConsentChange()}>
<BlockStack>
<Grid spacing="base">
<Checkbox
id="marketing"
checked={consentFormValues.marketing}
onChange={getCheckboxOnChangeHandler(
'marketing',
)}
>
Marketing
</Checkbox>
<Checkbox
id="analytics"
checked={consentFormValues.analytics}
onChange={getCheckboxOnChangeHandler(
'analytics',
)}
>
Analytics
</Checkbox>
<Checkbox
id="preferences"
checked={
consentFormValues.preferences
}
onChange={getCheckboxOnChangeHandler(
'preferences',
)}
>
Preferences
</Checkbox>
<Checkbox
id="saleOfData"
checked={consentFormValues.saleOfData}
onChange={getCheckboxOnChangeHandler(
'saleOfData',
)}
>
Sale of data
</Checkbox>
</Grid>
<Button accessibilityRole="submit">
Save
</Button>
</BlockStack>
</Form>
);
return (
<Sheet
id={sheetId}
accessibilityLabel="A sheet that collects privacy consent preferences"
defaultOpen={shouldShowBanner}
primaryAction={
<>
<Button
kind="secondary"
onPress={() =>
handleConsentChange({
analytics: false,
marketing: false,
preferences: false,
saleOfData: false,
})
}
>
I decline
</Button>
<Button
kind="secondary"
onPress={() =>
handleConsentChange({
analytics: true,
marketing: true,
preferences: true,
saleOfData: true,
})
}
>
I agree
</Button>
</>
}
secondaryAction={
<Button
kind="plain"
overlay={
<Modal id={modalId} padding>
{consentFormMarkup}
</Modal>
}
>
Settings
</Button>
}
>
<TextBlock>
This website uses cookies to ensure you
get the best experience on our website.{' '}
<Link>Privacy Policy</Link>
</TextBlock>
</Sheet>
);
}
import {
BlockStack,
Button,
Checkbox,
extension,
Form,
Grid,
Link,
Modal,
Sheet,
TextBlock,
} from '@shopify/ui-extensions/checkout';
import type {VisitorConsent} from '@shopify/ui-extensions/checkout';
// 1. Choose an extension target
export default extension(
'purchase.checkout.footer.render-after',
(
root,
{
applyTrackingConsentChange,
customerPrivacy,
ui,
},
) => {
const sheetId = 'sheet-consent';
const modalId = 'modal-consent';
let showBanner =
customerPrivacy.current.shouldShowBanner;
const formValues: VisitorConsent = {
analytics: undefined,
marketing: undefined,
preferences: undefined,
saleOfData: undefined,
};
// 2. Subscribe to customer privacy consent values and update component state when customerPrivacy changes
customerPrivacy.subscribe((value) => {
if (!value) {
return;
}
showBanner = value.shouldShowBanner;
const {
visitorConsent: {
analytics,
marketing,
preferences,
saleOfData,
},
} = value;
formValues.analytics =
value.visitorConsent.analytics;
formValues.marketing =
value.visitorConsent.marketing;
formValues.preferences =
value.visitorConsent.preferences;
formValues.saleOfData =
value.visitorConsent.saleOfData;
analyticsCheckbox.updateProps({
checked: analytics,
});
marketingCheckbox.updateProps({
checked: marketing,
});
preferencesCheckbox.updateProps({
checked: preferences,
});
saleOfDataCheckbox.updateProps({
checked: saleOfData,
});
});
// 3. Set up event handlers
const handleConsentChange = async (
visitorConsent?: VisitorConsent,
) => {
const result =
await applyTrackingConsentChange({
...(visitorConsent
? visitorConsent
: formValues),
type: 'changeVisitorConsent',
});
// Check if operation was successful
if (result.type === 'success') {
ui.overlay.close(modalId);
ui.overlay.close(sheetId);
} else {
// Handle failure case here
}
};
// 4. Create consent preferences form
const analyticsCheckbox =
root.createComponent(
Checkbox,
{
checked: formValues.analytics,
onChange: (checked: boolean) => {
formValues.analytics = checked;
},
},
'Analytics',
);
const marketingCheckbox =
root.createComponent(
Checkbox,
{
checked: formValues.marketing,
onChange: (checked: boolean) => {
formValues.marketing = checked;
},
},
'Marketing',
);
const preferencesCheckbox =
root.createComponent(
Checkbox,
{
checked: formValues.preferences,
onChange: (checked: boolean) => {
formValues.preferences = checked;
},
},
'Preferences',
);
const saleOfDataCheckbox =
root.createComponent(
Checkbox,
{
checked: formValues.saleOfData,
onChange: (checked: boolean) => {
formValues.saleOfData = checked;
},
},
'Sale of data',
);
const consentFormSubmitButton =
root.createComponent(
Button,
{accessibilityRole: 'submit'},
'Save',
);
const consentForm = root.createComponent(
Form,
{
onSubmit: () => handleConsentChange(),
},
);
const consentFormBlockStack =
root.createComponent(BlockStack);
const consentFormGrid = root.createComponent(
Grid,
{
spacing: 'base',
},
);
consentFormGrid.appendChild(
analyticsCheckbox,
);
consentFormGrid.appendChild(
marketingCheckbox,
);
consentFormGrid.appendChild(
preferencesCheckbox,
);
consentFormGrid.appendChild(
saleOfDataCheckbox,
);
consentFormGrid.appendChild(
consentFormSubmitButton,
);
consentFormBlockStack.appendChild(
consentFormGrid,
);
consentFormBlockStack.appendChild(
consentFormSubmitButton,
);
consentForm.appendChild(
consentFormBlockStack,
);
// 5. Create modal to display consent form
const modalFragment = root.createFragment();
const modal = root.createComponent(
Modal,
{
id: modalId,
padding: true,
},
[consentForm],
);
modalFragment.appendChild(modal);
const declineButton = root.createComponent(
Button,
{
kind: 'secondary',
onPress: () =>
handleConsentChange({
analytics: false,
marketing: false,
preferences: false,
saleOfData: false,
}),
},
'I decline',
);
const acceptButton = root.createComponent(
Button,
{
kind: 'secondary',
onPress: () =>
handleConsentChange({
analytics: true,
marketing: true,
preferences: true,
saleOfData: true,
}),
},
'I agree',
);
const settingsButton = root.createComponent(
Button,
{
kind: 'plain',
overlay: modalFragment,
},
'Settings',
);
const primaryActionFragment =
root.createFragment();
primaryActionFragment.appendChild(
declineButton,
);
primaryActionFragment.appendChild(
acceptButton,
);
const secondaryActionFragment =
root.createFragment();
secondaryActionFragment.appendChild(
settingsButton,
);
// 6. Create sheet to display privacy consent banner
const sheet = root.createComponent(
Sheet,
{
id: sheetId,
defaultOpen: showBanner,
accessibilityLabel:
'A sheet that collects privacy consent preferences',
primaryAction: primaryActionFragment,
secondaryAction: secondaryActionFragment,
},
[
root.createComponent(
TextBlock,
undefined,
[
'This website uses cookies to ensure you get the best experience on our website test.',
' ',
root.createComponent(
Link,
undefined,
'Privacy Policy',
),
],
),
],
);
root.appendChild(sheet);
},
);
import {useState} from 'react';
import {
reactExtension,
BlockStack,
Button,
Checkbox,
Form,
Grid,
Link,
Modal,
Sheet,
TextBlock,
useApi,
useCustomerPrivacy,
} from '@shopify/ui-extensions-react/checkout';
import type {VisitorConsent} from '@shopify/ui-extensions/checkout';
export default reactExtension(
'purchase.checkout.footer.render-after',
() => <Extension />,
);
function Extension() {
const {applyTrackingConsentChange, ui} =
useApi();
const {
shouldShowBanner,
visitorConsent: {
analytics,
marketing,
preferences,
saleOfData,
},
} = useCustomerPrivacy();
const [
consentFormValues,
setConsentFormValues,
] = useState({
analytics,
marketing,
preferences,
saleOfData,
});
const sheetId = 'sheet-consent';
const modalId = 'modal-consent';
const getCheckboxOnChangeHandler = (
key: string,
) => {
return function (checked: boolean) {
setConsentFormValues({
...consentFormValues,
[key]: checked,
});
};
};
const handleConsentChange = async (
visitorConsent?: VisitorConsent,
) => {
try {
const result =
await applyTrackingConsentChange({
...(visitorConsent
? visitorConsent
: consentFormValues),
type: 'changeVisitorConsent',
});
// Check if operation was successful
if (result.type === 'success') {
ui.overlay.close(modalId);
ui.overlay.close(sheetId);
} else {
// Handle failure case here
}
} catch (error) {
// Handle error case here
}
};
const consentFormMarkup = (
<Form onSubmit={() => handleConsentChange()}>
<BlockStack>
<Grid spacing="base">
<Checkbox
id="marketing"
checked={consentFormValues.marketing}
onChange={getCheckboxOnChangeHandler(
'marketing',
)}
>
Marketing
</Checkbox>
<Checkbox
id="analytics"
checked={consentFormValues.analytics}
onChange={getCheckboxOnChangeHandler(
'analytics',
)}
>
Analytics
</Checkbox>
<Checkbox
id="preferences"
checked={
consentFormValues.preferences
}
onChange={getCheckboxOnChangeHandler(
'preferences',
)}
>
Preferences
</Checkbox>
<Checkbox
id="saleOfData"
checked={consentFormValues.saleOfData}
onChange={getCheckboxOnChangeHandler(
'saleOfData',
)}
>
Sale of data
</Checkbox>
</Grid>
<Button accessibilityRole="submit">
Save
</Button>
</BlockStack>
</Form>
);
return (
<Sheet
id={sheetId}
accessibilityLabel="A sheet that collects privacy consent preferences"
defaultOpen={shouldShowBanner}
primaryAction={
<>
<Button
kind="secondary"
onPress={() =>
handleConsentChange({
analytics: false,
marketing: false,
preferences: false,
saleOfData: false,
})
}
>
I decline
</Button>
<Button
kind="secondary"
onPress={() =>
handleConsentChange({
analytics: true,
marketing: true,
preferences: true,
saleOfData: true,
})
}
>
I agree
</Button>
</>
}
secondaryAction={
<Button
kind="plain"
overlay={
<Modal id={modalId} padding>
{consentFormMarkup}
</Modal>
}
>
Settings
</Button>
}
>
<TextBlock>
This website uses cookies to ensure you
get the best experience on our website.{' '}
<Link>Privacy Policy</Link>
</TextBlock>
</Sheet>
);
}
import {
BlockStack,
Button,
Checkbox,
extension,
Form,
Grid,
Link,
Modal,
Sheet,
TextBlock,
} from '@shopify/ui-extensions/checkout';
import type {VisitorConsent} from '@shopify/ui-extensions/checkout';
// 1. Choose an extension target
export default extension(
'purchase.checkout.footer.render-after',
(
root,
{
applyTrackingConsentChange,
customerPrivacy,
ui,
},
) => {
const sheetId = 'sheet-consent';
const modalId = 'modal-consent';
let showBanner =
customerPrivacy.current.shouldShowBanner;
const formValues: VisitorConsent = {
analytics: undefined,
marketing: undefined,
preferences: undefined,
saleOfData: undefined,
};
// 2. Subscribe to customer privacy consent values and update component state when customerPrivacy changes
customerPrivacy.subscribe((value) => {
if (!value) {
return;
}
showBanner = value.shouldShowBanner;
const {
visitorConsent: {
analytics,
marketing,
preferences,
saleOfData,
},
} = value;
formValues.analytics =
value.visitorConsent.analytics;
formValues.marketing =
value.visitorConsent.marketing;
formValues.preferences =
value.visitorConsent.preferences;
formValues.saleOfData =
value.visitorConsent.saleOfData;
analyticsCheckbox.updateProps({
checked: analytics,
});
marketingCheckbox.updateProps({
checked: marketing,
});
preferencesCheckbox.updateProps({
checked: preferences,
});
saleOfDataCheckbox.updateProps({
checked: saleOfData,
});
});
// 3. Set up event handlers
const handleConsentChange = async (
visitorConsent?: VisitorConsent,
) => {
const result =
await applyTrackingConsentChange({
...(visitorConsent
? visitorConsent
: formValues),
type: 'changeVisitorConsent',
});
// Check if operation was successful
if (result.type === 'success') {
ui.overlay.close(modalId);
ui.overlay.close(sheetId);
} else {
// Handle failure case here
}
};
// 4. Create consent preferences form
const analyticsCheckbox =
root.createComponent(
Checkbox,
{
checked: formValues.analytics,
onChange: (checked: boolean) => {
formValues.analytics = checked;
},
},
'Analytics',
);
const marketingCheckbox =
root.createComponent(
Checkbox,
{
checked: formValues.marketing,
onChange: (checked: boolean) => {
formValues.marketing = checked;
},
},
'Marketing',
);
const preferencesCheckbox =
root.createComponent(
Checkbox,
{
checked: formValues.preferences,
onChange: (checked: boolean) => {
formValues.preferences = checked;
},
},
'Preferences',
);
const saleOfDataCheckbox =
root.createComponent(
Checkbox,
{
checked: formValues.saleOfData,
onChange: (checked: boolean) => {
formValues.saleOfData = checked;
},
},
'Sale of data',
);
const consentFormSubmitButton =
root.createComponent(
Button,
{accessibilityRole: 'submit'},
'Save',
);
const consentForm = root.createComponent(
Form,
{
onSubmit: () => handleConsentChange(),
},
);
const consentFormBlockStack =
root.createComponent(BlockStack);
const consentFormGrid = root.createComponent(
Grid,
{
spacing: 'base',
},
);
consentFormGrid.appendChild(
analyticsCheckbox,
);
consentFormGrid.appendChild(
marketingCheckbox,
);
consentFormGrid.appendChild(
preferencesCheckbox,
);
consentFormGrid.appendChild(
saleOfDataCheckbox,
);
consentFormGrid.appendChild(
consentFormSubmitButton,
);
consentFormBlockStack.appendChild(
consentFormGrid,
);
consentFormBlockStack.appendChild(
consentFormSubmitButton,
);
consentForm.appendChild(
consentFormBlockStack,
);
// 5. Create modal to display consent form
const modalFragment = root.createFragment();
const modal = root.createComponent(
Modal,
{
id: modalId,
padding: true,
},
[consentForm],
);
modalFragment.appendChild(modal);
const declineButton = root.createComponent(
Button,
{
kind: 'secondary',
onPress: () =>
handleConsentChange({
analytics: false,
marketing: false,
preferences: false,
saleOfData: false,
}),
},
'I decline',
);
const acceptButton = root.createComponent(
Button,
{
kind: 'secondary',
onPress: () =>
handleConsentChange({
analytics: true,
marketing: true,
preferences: true,
saleOfData: true,
}),
},
'I agree',
);
const settingsButton = root.createComponent(
Button,
{
kind: 'plain',
overlay: modalFragment,
},
'Settings',
);
const primaryActionFragment =
root.createFragment();
primaryActionFragment.appendChild(
declineButton,
);
primaryActionFragment.appendChild(
acceptButton,
);
const secondaryActionFragment =
root.createFragment();
secondaryActionFragment.appendChild(
settingsButton,
);
// 6. Create sheet to display privacy consent banner
const sheet = root.createComponent(
Sheet,
{
id: sheetId,
defaultOpen: showBanner,
accessibilityLabel:
'A sheet that collects privacy consent preferences',
primaryAction: primaryActionFragment,
secondaryAction: secondaryActionFragment,
},
[
root.createComponent(
TextBlock,
undefined,
[
'This website uses cookies to ensure you get the best experience on our website test.',
' ',
root.createComponent(
Link,
undefined,
'Privacy Policy',
),
],
),
],
);
root.appendChild(sheet);
},
);