Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Add Shopify OS 2.0 integration page #558

Merged
merged 2 commits into from
Feb 24, 2024
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
1 change: 1 addition & 0 deletions docs/_table-of-contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- [React](/react)
- [Remix](/remix)
- [Shopify Hydrogen](/shopify-hydrogen)
- [Shopify OS 2.0](/shopify-os2)
- [Solid](/solid)
- [SvelteKit](/sveltekit)
- [Configuration](/configuration)
Expand Down
102 changes: 102 additions & 0 deletions docs/shopify-os2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
title: Shopify OS 2.0
---

Shopify OS 2.0 frontends are built with the Liquid templating language. Since it compiles to HTML, the [HTML integration guide](./html) is a good starting point. There are however a few platform based challenges and limitations you have to work around.

## Theme Files & Partydawn

The integration of Partytown largely depends on the theme you're using and the structure of its files. It's impossible to cover all different cases and you'll need to figure stuff out based on the examples given on this page. This should be viewed more as a template and not a full guide.

Shown code and steps are based on [Partydawn](https://github.com/j0Shi82/shopify-partydawn), an example of Shopify's Dawn theme that integrates Partytown.

## Requirements

- You need to be able to [set up an app proxy](https://github.com/edlaver/cloudflare-worker-partytown-shopify-app-proxy/tree/main) that can serve partytown assets
- Learn the basics of [Shopify Web Pixels](https://shopify.dev/docs/apps/marketing/pixels)

### Can't Hurt

- [Shopify's Customer Privacy API](https://shopify.dev/docs/api/consent-tracking)
- Experience with cookie consent management
- Make yourself comfortable with your theme's structure and file system

## Install

In the root file of your theme, add the Partytown snippet and config object. This most likely is the file that contains the `{{ contents_for_header }}` variable.

```html
<script>
window.partytown = {
forward: ['dataLayer.push' /**, ... */],
// you cannot host partytown within Shopify OS 2.0
// so you need to proxy assets
resolveUrl: function (url, location, type) {
if (type === 'script' /** extend as needed */) {
const proxyUrl = new URL('/apps/partytown/proxy', location.href);
proxyUrl.searchParams.append('url', url.href);
return proxyUrl;
}
return url;
};
};
</script>
<script>
/* Inlined Partytown Snippet */
</script>
```

It's important to understand that you cannot host Partytown assets on Shopify OS 2.0. So your app [proxy](./proxying-requests) needs to serve them.

Then add the `type="text/partytown"` attribute for each script that should run from the web worker as usual.

## Hooking Into the Web Pixel

Tracking events in Shopify isn't as straight forward as with other integrations. The checkout is fully controlled by Shopify, meaning you cannot fire some important events manually (`purchase`). Additionally some customization to your theme has to be done to find the right places to track `cart` events etc. The easiest way to deal with tracking in Shopify is through Web Pixels, which offer an API to hook into Shopify's own event stream.

You however cannot get Partytown to work inside Web Pixels because it's a sandboxed environment in an iframe without access to the top frame. To install the Tag Manager inside a Pixel you can follow [the official documentation](https://help.shopify.com/en/manual/promoting-marketing/pixels/custom-pixels/gtm-tutorial). The approach should be similar with other vendors.

The only exception is that you don't want to load the tracking scripts directly in the sandbox. Instead we inject the script as `text/partytown` in the regular page and create an event bridge between the Pixel and the main page using the `sessionStorage` that is available in the Pixel. We push events to the storage, pick them up on the main page, and forward them to Partytown.

```js
// inside the Pixel
browser.sessionStorage.setItem(`pt_dl_` + inc++, JSON.stringify(data));

// on the main page
function isSessionStorageAvailable() {
var test = 'test';
try {
sessionStorage.setItem(test, test);
sessionStorage.removeItem(test);
return true;
} catch (e) {
return false;
}
}

if (isSessionStorageAvailable()) {
var inc = 0;
window.setInterval(function () {
while (sessionStorage.getItem('pt_dl_' + inc) !== null) {
window.dataLayer.push(JSON.parse(sessionStorage.getItem('pt_dl_' + inc)));
sessionStorage.removeItem('pt_dl_' + inc);
inc++;
}
// adjust the interval as you see fit
}, 1000);
}
```

In case sessionStorage is not available, the Pixel should install the tracking scripts conventionally. This also has to be done on checkout pages which don't work with Partytown. You have all the tools inside Pixels to perform these checks and operations.

You can certainly also use `window.postMessage` to build an event bridge between the Web Pixel (iframe) and the top frame. But `sessionStorage` is directly supported by the Web Pixel API.

## Tracking Outside the Web Pixels

You can still implement manual tracking outside Web Pixels. In that case you need to add additional tracking inside `Settings-> Checkout -> Order Status Page Additional Scripts` to track purchases and conversions. Please note that this is deprecated. The current deadline is August 28, 2025. Although Shopify has pushed it back multiple times, you will eventually have to switch to Web Pixels or [Checkout Extensions](https://shopify.dev/docs/api/checkout-extensions).

## Use the Mutation Observer to Move Scripts into the Worker

Unfortunately you don't have full control over all scripts that are injected into the site. Some stuff will get added by Shopify automatically. We can thankfully still manipulate inline scripts using a [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver). Changing the type to text/partytown will prevent script execution and the script will instead be processed by Partytown.

This way you can offload Shopify's own `trekkie` tracking and run the standard integrations for [Google Analytics](https://help.shopify.com/en/manual/reports-and-analytics/google-analytics/google-analytics-setup) and [Facebook](https://help.shopify.com/en/manual/promoting-marketing/analyze-marketing/meta-pixel) through Partytown.
9 changes: 2 additions & 7 deletions src/lib/sandbox/read-main-platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ import {
serializeConfig,
} from '../utils';
import { config, docImpl, libPath, mainWindow } from './main-globals';
import {
InterfaceType,
InterfaceInfo,
InterfaceMember,
InitWebWorkerData,
} from '../types';
import { InterfaceType, InterfaceInfo, InterfaceMember, InitWebWorkerData } from '../types';

export const readMainPlatform = () => {
const elm = docImpl.createElement('i');
Expand Down Expand Up @@ -66,7 +61,7 @@ export const readMainPlatform = () => {
$config$,
$interfaces$: readImplementations(impls, initialInterfaces),
$libPath$: new URL(libPath, mainWindow.location as any) + '',
$origin$: origin
$origin$: origin,
};

addGlobalConstructorUsingPrototype(
Expand Down
5 changes: 1 addition & 4 deletions src/lib/web-worker/init-web-worker.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
commaSplit,
webWorkerCtx,
} from './worker-constants';
import { commaSplit, webWorkerCtx } from './worker-constants';
import type { InitWebWorkerData, PartytownInternalConfig } from '../types';

export const initWebWorker = (initWebWorkerData: InitWebWorkerData) => {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/web-worker/worker-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const addStorageApi = (

get length() {
if (isSameOrigin) {
return getter(win, [storageName, 'length'])
return getter(win, [storageName, 'length']);
} else {
warnCrossOrigin('length', storageName, env);
}
Expand Down
Loading