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
15 changes: 15 additions & 0 deletions .changeset/visitor-consent-incontext.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@shopify/hydrogen': minor
'@shopify/hydrogen-react': minor
---

Add `visitorConsent` support to `@inContext` directive for Storefront API parity

**Note: Most Hydrogen storefronts do NOT need this feature.**

This API addition provides Storefront API 2025-10 parity for the `visitorConsent` parameter in `@inContext` directives. However, if you're using Hydrogen's analytics provider or Shopify's Customer Privacy API (including third-party consent services integrated with it), consent is already handled automatically and you don't need to use this.

This feature is primarily intended for Checkout Kit and other non-Hydrogen integrations that manage consent outside of Shopify's standard consent flow.

**What it does:**
When explicitly provided, `visitorConsent` encodes buyer consent preferences (analytics, marketing, preferences, saleOfData) into the cart's `checkoutUrl` via the `_cs` parameter.
72 changes: 63 additions & 9 deletions packages/hydrogen-react/src/cart-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ export const CartLineAdd = (cartFragment: string): string => /* GraphQL */ `
$numCartLines: Int = 250
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
)
@inContext(
country: $country
language: $language
visitorConsent: $visitorConsent
) {
cartLinesAdd(cartId: $cartId, lines: $lines) {
cart {
...CartFragment
Expand All @@ -22,7 +28,13 @@ export const CartCreate = (cartFragment: string): string => /* GraphQL */ `
$numCartLines: Int = 250
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
)
@inContext(
country: $country
language: $language
visitorConsent: $visitorConsent
) {
cartCreate(input: $input) {
cart {
...CartFragment
Expand All @@ -40,7 +52,13 @@ export const CartLineRemove = (cartFragment: string): string => /* GraphQL */ `
$numCartLines: Int = 250
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
)
@inContext(
country: $country
language: $language
visitorConsent: $visitorConsent
) {
cartLinesRemove(cartId: $cartId, lineIds: $lines) {
cart {
...CartFragment
Expand All @@ -58,7 +76,13 @@ export const CartLineUpdate = (cartFragment: string): string => /* GraphQL */ `
$numCartLines: Int = 250
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
)
@inContext(
country: $country
language: $language
visitorConsent: $visitorConsent
) {
cartLinesUpdate(cartId: $cartId, lines: $lines) {
cart {
...CartFragment
Expand All @@ -76,7 +100,13 @@ export const CartNoteUpdate = (cartFragment: string): string => /* GraphQL */ `
$numCartLines: Int = 250
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
)
@inContext(
country: $country
language: $language
visitorConsent: $visitorConsent
) {
cartNoteUpdate(cartId: $cartId, note: $note) {
cart {
...CartFragment
Expand All @@ -96,7 +126,13 @@ export const CartBuyerIdentityUpdate = (
$numCartLines: Int = 250
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
)
@inContext(
country: $country
language: $language
visitorConsent: $visitorConsent
) {
cartBuyerIdentityUpdate(cartId: $cartId, buyerIdentity: $buyerIdentity) {
cart {
...CartFragment
Expand All @@ -116,7 +152,13 @@ export const CartAttributesUpdate = (
$numCartLines: Int = 250
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
)
@inContext(
country: $country
language: $language
visitorConsent: $visitorConsent
) {
cartAttributesUpdate(attributes: $attributes, cartId: $cartId) {
cart {
...CartFragment
Expand All @@ -136,7 +178,13 @@ export const CartDiscountCodesUpdate = (
$numCartLines: Int = 250
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
)
@inContext(
country: $country
language: $language
visitorConsent: $visitorConsent
) {
cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) {
cart {
...CartFragment
Expand All @@ -153,7 +201,13 @@ export const CartQuery = (cartFragment: string): string => /* GraphQL */ `
$numCartLines: Int = 250
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
)
@inContext(
country: $country
language: $language
visitorConsent: $visitorConsent
) {
cart(id: $id) {
...CartFragment
}
Expand Down
16 changes: 16 additions & 0 deletions packages/hydrogen/src/cart/queries/cart-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
MetafieldsSetUserError,
MetafieldDeleteUserError,
CartWarning,
VisitorConsent,
} from '@shopify/hydrogen-react/storefront-api-types';
import type {StorefrontApiErrors, Storefront} from '../../storefront';
import {CustomerAccount} from '../../customer/types';
Expand All @@ -28,6 +29,21 @@ export type CartOptionalInput = {
* @default storefront.i18n.language
*/
language?: LanguageCode;
/**
* Visitor consent preferences for the Storefront API's @inContext directive.
*
* **Most Hydrogen storefronts do NOT need this.** If you're using Hydrogen's
* analytics provider or Shopify's Customer Privacy API (including third-party
* consent services integrated with it), consent is handled automatically.
*
* This option exists for Storefront API parity and is primarily intended for
* non-Hydrogen integrations like Checkout Kit that manage consent outside
* Shopify's standard consent flow.
*
* When provided, consent is encoded into the cart's checkoutUrl via the _cs parameter.
* @see https://shopify.dev/docs/storefronts/headless/building-with-the-storefront-api/in-context
*/
visitorConsent?: VisitorConsent;
};

export type MetafieldWithoutOwnerId = Omit<CartMetafieldsSetInput, 'ownerId'>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function cartAttributesUpdateDefault(
variables: {
cartId: optionalParams?.cartId || options.getCartId(),
attributes,
...optionalParams,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this was missing before. it's present in the other ones

},
});
return formatAPIResult(cartAttributesUpdate, errors);
Expand All @@ -40,7 +41,10 @@ export const CART_ATTRIBUTES_UPDATE_MUTATION = (
mutation cartAttributesUpdate(
$cartId: ID!
$attributes: [AttributeInput!]!
) {
$language: LanguageCode
$country: CountryCode
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartAttributesUpdate(cartId: $cartId, attributes: $attributes) {
cart {
...CartApiMutation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ export const CART_BUYER_IDENTITY_UPDATE_MUTATION = (
$buyerIdentity: CartBuyerIdentityInput!
$language: LanguageCode
$country: CountryCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartBuyerIdentityUpdate(cartId: $cartId, buyerIdentity: $buyerIdentity) {
cart {
...CartApiMutation
Expand Down
3 changes: 2 additions & 1 deletion packages/hydrogen/src/cart/queries/cartCreateDefault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ export const CART_CREATE_MUTATION = (
$input: CartInput!
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartCreate(input: $input) {
cart {
...CartApiMutation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ export const CART_DELIVERY_ADDRESSES_ADD_MUTATION = (
$addresses: [CartSelectableAddressInput!]!,
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartDeliveryAddressesAdd(addresses: $addresses, cartId: $cartId) {
cart {
...CartApiMutation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ export const CART_DELIVERY_ADDRESSES_REMOVE_MUTATION = (
$addressIds: [ID!]!,
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartDeliveryAddressesRemove(addressIds: $addressIds, cartId: $cartId) {
cart {
...CartApiMutation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ export const CART_DELIVERY_ADDRESSES_UPDATE_MUTATION = (
$addresses: [CartSelectableAddressUpdateInput!]!,
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartDeliveryAddressesUpdate(addresses: $addresses, cartId: $cartId) {
cart {
...CartApiMutation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ export const CART_DISCOUNT_CODE_UPDATE_MUTATION = (
$discountCodes: [String!]
$language: LanguageCode
$country: CountryCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) {
... @defer {
cart {
Expand Down
18 changes: 17 additions & 1 deletion packages/hydrogen/src/cart/queries/cartGetDefault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
Cart,
CountryCode,
LanguageCode,
VisitorConsent,
} from '@shopify/hydrogen-react/storefront-api-types';

type CartGetProps = {
Expand All @@ -28,6 +29,20 @@ type CartGetProps = {
* @default 100
*/
numCartLines?: number;
/**
* Visitor consent preferences for the Storefront API's @inContext directive.
*
* **Most Hydrogen storefronts do NOT need this.** If you're using Hydrogen's
* analytics provider or Shopify's Customer Privacy API (including third-party
* consent services integrated with it), consent is handled automatically.
*
* This option exists for Storefront API parity and is primarily intended for
* non-Hydrogen integrations like Checkout Kit that manage consent outside
* Shopify's standard consent flow.
*
* When provided, consent is encoded into the cart's checkoutUrl via the _cs parameter.
*/
visitorConsent?: VisitorConsent;
};

export type CartGetFunction = (
Expand Down Expand Up @@ -77,7 +92,8 @@ const CART_QUERY = (cartFragment = DEFAULT_CART_FRAGMENT) => `#graphql
$numCartLines: Int = 100
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cart(id: $cartId) {
...CartApiQuery
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ export const CART_GIFT_CARD_CODE_UPDATE_MUTATION = (
$giftCardCodes: [String!]!
$language: LanguageCode
$country: CountryCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartGiftCardCodesUpdate(cartId: $cartId, giftCardCodes: $giftCardCodes) {
cart {
...CartApiMutation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export const CART_GIFT_CARD_CODES_REMOVE_MUTATION = (
$appliedGiftCardIds: [ID!]!
$language: LanguageCode
$country: CountryCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartGiftCardCodesRemove(cartId: $cartId, appliedGiftCardIds: $appliedGiftCardIds) {
cart {
...CartApiMutation
Expand Down
3 changes: 2 additions & 1 deletion packages/hydrogen/src/cart/queries/cartLinesAddDefault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ export const CART_LINES_ADD_MUTATION = (
$lines: [CartLineInput!]!
$country: CountryCode = ZZ
$language: LanguageCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartLinesAdd(cartId: $cartId, lines: $lines) {
cart {
...CartApiMutation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ export const CART_LINES_REMOVE_MUTATION = (
$lineIds: [ID!]!
$language: LanguageCode
$country: CountryCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
cart {
...CartApiMutation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ export const CART_LINES_UPDATE_MUTATION = (
$lines: [CartLineUpdateInput!]!
$language: LanguageCode
$country: CountryCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartLinesUpdate(cartId: $cartId, lines: $lines) {
cart {
...CartApiMutation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function cartMetafieldDeleteDefault(
ownerId,
key,
},
...optionalParams,
},
});
return formatAPIResult(
Expand All @@ -49,7 +50,10 @@ export function cartMetafieldDeleteDefault(
export const CART_METAFIELD_DELETE_MUTATION = () => `#graphql
mutation cartMetafieldDelete(
$input: CartMetafieldDeleteInput!
) {
$language: LanguageCode
$country: CountryCode
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartMetafieldDelete(input: $input) {
userErrors {
code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function cartMetafieldsSetDefault(
};
errors: StorefrontApiErrors;
}>(CART_METAFIELD_SET_MUTATION(), {
variables: {metafields: metafieldsWithOwnerId},
variables: {metafields: metafieldsWithOwnerId, ...optionalParams},
});

return formatAPIResult(
Expand All @@ -53,7 +53,8 @@ export const CART_METAFIELD_SET_MUTATION = () => `#graphql
$metafields: [CartMetafieldsSetInput!]!
$language: LanguageCode
$country: CountryCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartMetafieldsSet(metafields: $metafields) {
userErrors {
code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export const CART_NOTE_UPDATE_MUTATION = (
$note: String!
$language: LanguageCode
$country: CountryCode
) @inContext(country: $country, language: $language) {
$visitorConsent: VisitorConsent
) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) {
cartNoteUpdate(cartId: $cartId, note: $note) {
cart {
...CartApiMutation
Expand Down
Loading
Loading