Skip to content

Add opt-in account widget loading to ShopifyScripts #4006

Description

@fredericoo

Parent

Context

Shopify's <shopify-account> web component shows the customer's avatar and account sheet. The code for it lives in a separate script on Shopify's CDN. Right now Hydrogen users have to add that <script> tag by hand.

This task adds an accountWidget switch to ShopifyScripts so Hydrogen adds the script for you. Loading the script only teaches the browser what <shopify-account> is. It does not put an account widget on the page - that's still up to the developer.

How ShopifyScripts works (read this first)

  • Core - packages/hydrogen/src/core/shopify-scripts/index.ts has one function, getShopifyScriptTags(), that decides which <script> tags a storefront needs. It returns a plain list: URL + attributes for each script. This is the only place that decides what loads.
  • React (packages/hydrogen/src/react/shopify-scripts.tsx) and Vue (packages/hydrogen/src/vue/shopify-scripts.ts) just take that list and print the tags. They make no decisions.

So: the real change is in core. React needs no code change (it passes all options straight through). Vue needs one line because Vue requires every prop to be declared - the build will fail until you add it, which is intentional.

Use the existing inbox option as your template at every step.

What to build

Add an optional accountWidget flag. When true, output:

<script
  id="shopify-account-widget"
  type="module"
  async
  crossorigin="anonymous"
  src="https://cdn.shopify.com/storefront/web-components/account.js"
></script>

Decisions already made:

  • async: yes (same as the Inbox script).
  • id: shopify-account-widget (every other script has an id; keep the convention).
  • Position: add it last in the list. Order isn't important for this script.

Steps

  1. Constant - in core/shopify-scripts/constants.ts, add SHOPIFY_ACCOUNT_WIDGET_SCRIPT next to SHOPIFY_INBOX_SCRIPT. Export it from core/shopify-scripts/index.ts so tests import it instead of copy-pasting the URL.
  2. Option type - in core/shopify-scripts/types.ts, add accountWidget?: boolean next to inbox. Write a short TSDoc comment: loads the <shopify-account> element definition, does not render it.
  3. Core logic - in getShopifyScriptTags(), default it to false, and add an if (accountWidget) block after the Inbox one that pushes the descriptor above. Spread the existing nonceAttributes so CSP nonces work like every other script.
  4. Vue - in vue/shopify-scripts.ts, declare accountWidget: { type: Boolean, default: undefined } in the props list (copy the inbox line).
  5. React - no change needed. Do not add shopify-account to the JSX element types; rendering the element is out of scope.
  6. Tests - see below.
  7. Docs - mention the opt-in in packages/hydrogen/skills/hydrogen-setup/steps/7-shopify-runtime-scripts.md, written for someone using Hydrogen (not someone building it).
  8. Changeset - add .changeset/<name>.md with '@shopify/hydrogen': minor (see accept-variant-id.md for the shape).

Tests

Test the decision in core and the printing in React/Vue. Don't repeat the same checks in three places.

  • core/shopify-scripts.test.ts: copy the Inbox tests. accountWidget: true -> the exact descriptor is in the list, including the nonce. Default -> it is not in the list. Add accountWidget: true to the "preserves an explicitly empty nonce" test and bump its expected length.
  • react/shopify-scripts.test.tsx and vue/shopify-scripts.test.ts: add accountWidget: true to the existing big SSR render test and assert the HTML contains the account script tag. That's enough.
  • Use assert from core/test-utils.ts. Never use !.

Try it by hand in the React Router template

  1. Open templates/react-router/app/root.tsx. Find the <ShopifyScripts ... /> tag in the <head> and add accountWidget to it:

    <ShopifyScripts
      i18n={storefrontConfig.i18n}
      shop={shop}
      consent={analyticsConsent}
      navigate={navigate}
      routes={routeTemplates}
      accountWidget
    />
  2. Put the widget somewhere visible. In the same file, inside <body> just above {children}, paste plain HTML (this is not React-specific - it's just custom HTML tags):

    <shopify-store
      store-domain="your-store.myshopify.com"
      public-access-token="your-public-access-token"
      country="US"
      language="EN"
    >
      <shopify-account></shopify-account>
    </shopify-store>

    TypeScript will complain that it doesn't know these tags. That's expected and fine for a local test - ignore it, or add {/* @ts-expect-error */} above each tag. Don't commit this.

  3. Run pnpm --filter @shopify/hydrogen-template-react-router dev, open the page and check:

    • View source: the account.js script tag is in <head>.
    • In the browser console, customElements.get('shopify-account') returns a function (not undefined). This proves the script loaded and defined the element.
    • Remove accountWidget and reload: the tag is gone and customElements.get('shopify-account') is undefined.
  4. To see the actual avatar you need a real store with the Customer Account API enabled and a public access token from the Headless channel - mock.shop (the template default) does not support it. Set PUBLIC_STORE_DOMAIN etc. in .env per the template README. The account component guide covers the store-side setup.

Acceptance criteria

  • ShopifyScriptTagsOptions includes accountWidget?: boolean with TSDoc saying it loads the element but does not render it.
  • React and Vue ShopifyScripts accept the same accountWidget prop.
  • getShopifyScriptTags() and renderShopifyScriptTags() include the account script (with id, type="module", async, crossorigin="anonymous", nonce) when accountWidget is true.
  • Nothing is added by default or when false.
  • React and Vue SSR output include the script when the flag is on.
  • Tests cover on / off / nonce in core; React and Vue each check the rendered HTML once.
  • hydrogen-setup skill mentions the opt-in.
  • Minor changeset added.

Out of scope

  • Rendering <shopify-account> or <shopify-store> for the user.
  • Customer Account API or OAuth setup.
  • React/Vue wrappers for the account widget.

Blocked by

None.

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions