Custom Integrations
Custom storefronts can use the SDK's offer and modal components, build a completely custom personalization experience, or combine the two. In every case, the SDK provides an authenticated GraphQL client for Stylux API requests.
If you use the built-in modal, provide a cart adapter. The SDK will create the bundle, resolve the products to add, and write them through the adapter. Subscribing to BUNDLE_CREATE to write the cart is a legacy integration pattern.
Initializing the SDK
Load the SDK asynchronously, then set it up with your merchant details and cart adapter:
await Stylux.setup({
apiKey: 'STLX_key-for-unsigned-requests',
merchantId: 'merchant-id',
cart: {
adapter: cartAdapter,
},
});Preloading is enabled by default. If the integration does not use any built-in visual components, set preload to false. To preload only the V2 modal:
await Stylux.setup({
apiKey: 'STLX_key-for-unsigned-requests',
merchantId: 'merchant-id',
cart: {
adapter: cartAdapter,
},
preload: {
modals: ['MODAL_V2'],
},
});Getting Product/Offer Information
The query returns live offer candidates in product.liveOffers.nodes. Offers with an ACTIVE or MANUALLY_SET_TO_ACTIVE state can be shown to customers. AUTO_DISABLED offers may also be returned for SDK state handling and should not normally be selected by a custom experience.
const { product } = await Stylux.gql.queries.productWithOffers({
variables: {
id: 'stylux-product-id-or-merchant-product-id',
offerChannels: ['D2C'],
},
});The lookup accepts the internal Stylux product UUID or the external platform productId stored in Stylux. On Shopify, that external ID is the variant ID. The query defaults to the D2C channel; pass ['D2C', 'B2B'] when the integration needs both.
The query returns { product }. An offer in product.liveOffers.nodes looks like:
{
"id": "offer-id",
"activeState": "ACTIVE",
"modalConfig": {
// ...
},
"associatedProducts": {
"nodes": [
{
"type": "REPLACEMENT",
"product": {
"productId": "1234"
}
},
{
"type": "TEXT_UPSELL",
"product": {
"productId": "1234"
}
}
]
},
"placements": {
"nodes": [
{
"placementOptions": {
"nodes": [
{
"personalizationOption": {
"id": "option-id",
"optionConfig": {
// ...
}
}
}
]
}
}
]
},
"options": {
"nodes": [
{
"id": "option-id",
"optionConfig": {
// ...
}
}
]
}
}The important fields for custom integrations are options, placements, associatedProducts, incompatiblePlacements, and personalizationLimits. Use placements and placementOptions to collect personalization details within each option's constraints. For example, submitted text must satisfy its length, symbol, whitespace, and case constraints. Bundle creation also validates incompatible placements and offer placement limits.
Note: the Stylux SDK includes a built in GraphQL client under the hood with document level caching. This avoids making multiple GraphQL requests for queries that have already been successful.
Offer Option Types
A Stylux offer can contain zero or more options. The current option types are:
SIMPLE_TEXTSIMPLE_MULTIPLE_CHOICETEXT_FONTTEXT_COLORTEXT_PLACEMENTICONIMAGE_UPLOADMONOGRAMMONOGRAM_COLOR
The GraphQL result includes __typename on each option union member. Use it to narrow the corresponding optionConfig. Common option shapes are shown below.
SIMPLE_TEXT
SIMPLE_TEXTA SIMPLE_TEXT option has the following shape:
{
"optionConfig": {
"maxLength": 3,
"minLength": 1,
"ctaText": "Your initials",
"label": "Initials",
"placeholderText": "",
"allowWhitespace": true,
"allowedSymbols": ["!"],
"letterCase": "MIXED",
"inputType": "ALPHANUMERIC",
"previewText": "ABC"
}
}TEXT_FONT
TEXT_FONTA TEXT_FONT option has the following shape:
{
"optionConfig": {
"fontAssets": [
{
"displayName": "Custom Display Name",
"asset": {
"id": "font-asset-id",
"displayName": "Font",
"filename": "font.ttf",
"url": "https://example.com/font.ttf"
}
}
]
}
}TEXT_COLOR
TEXT_COLORA TEXT_COLOR option has the following shape:
{
"optionConfig": {
"options": [
{
"colorHexCode": "#00FF00",
"colorName": "Green"
},
{
"colorHexCode": "#0000FF",
"colorName": "Blue"
}
]
}
}ICON
ICONAn ICON option has the following shape:
{
"optionConfig": {
"categories": [
{
"categoryName": "DEFAULT",
"icons": [
{
"name": "Heart",
"displayName": "Heart",
"iconArtworkFile": {
"id": "id-1",
"displayName": null,
"filename": "heart.svg",
"url": "https://example.com/heart.svg",
"type": "IMAGE"
},
"previewImage": {
"id": "id-1",
"displayName": null,
"filename": "heart.svg",
"url": "https://example.com/heart.svg",
"type": "IMAGE"
},
"selectionIcon": {
"id": "id-1",
"displayName": null,
"filename": "heart.svg",
"url": "https://example.com/heart.svg",
"type": "IMAGE"
}
}
]
}
]
}
}Out of the Box Modal
Once you have retrieved a product and its live offers, you can use the built in modal to render the customer experience. Open the modal with an eligible offer:
(async function () {
await Stylux.modals.open({ offer, product });
})();product is the top level product from the productWithOffers result. offer is an eligible offer selected by channel, active state, and fulfillment type from product.liveOffers.nodes; do not assume the first node is the correct offer.
Custom User Experience
For a completely custom experience, capture the personalization details and create a bundle. Every value must satisfy the selected option, placement, and offer constraints.
The following example shows text, font, color, and icon entries. PersonalizationEntryInput also supports simpleMultipleChoice, textPlacement, imageUpload, monogram, and monogramColor.
Stylux.gql.mutations
.createBundle({
variables: {
input: {
entries: [
{
personalizationOptionId: 'text-option-id-from-offer',
simpleText: {
personalizationText: '123',
type: 'SIMPLE_TEXT',
placementId: 'placement-id',
},
},
{
personalizationOptionId: 'font-option-id-from-offer',
textFont: {
fontAssetId: 'font-asset-id',
type: 'TEXT_FONT',
},
},
{
personalizationOptionId: 'color-option-id-from-offer',
textColor: {
colorName: 'Blue',
type: 'TEXT_COLOR',
},
},
{
personalizationOptionId: 'icon-option-id-from-offer',
icon: {
type: 'ICON',
iconName: 'ICON_NAME',
placementId: 'placement-id',
categoryName: 'category-name-from-option-config',
},
},
],
merchantId: 'merchant-id',
offerId: 'offer-id',
},
},
})
.then(({ createBundle }) => {
const { id } = createBundle;
console.log({ bundleId: id });
});Directly calling createBundle is a fully custom flow. It does not open the Stylux modal, publish BUNDLE_CREATE, invoke SDK checkout, or populate SDK normalization storage. Your application is responsible for the complete cart mapping.
That mapping depends on the offer and submitted entries. It may include:
- The external platform
productIdfor the base or replacement product (on Shopify, the variant ID) - Generic, text, icon, image upload, or monogram upsell products
- Products selected from associated product price tiers by quantity
- Stylux bundle, attribution, product type, and personalization line properties
- Parent line relationships when the cart platform supports nested lines
Do not treat _STYLUX_BUNDLE as the only required property. Typed upsell products are selected from entries that include a placement. When fulfillmentType is UPSELL, include the associated UPSELL product; that base line does not receive _STYLUX_BUNDLE.
Fulfillment bundles with more than one line must remain at matching quantities. See Cart Balancing and Normalization. SDK normalization does not manage lines created through this direct mutation flow.
If you do not need a completely custom personalization form, prefer opening the built in modal with a registered cart adapter. The SDK will handle product resolution, line properties, add to cart, and normalization.
Updated about 1 hour ago
