Shopify / Shopify/hydrogen

Add React and Vue ShopifyAccountWidget bindings

Open
#4,008 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
2.1k
Forks
443
Avg merge
4d 19h
Merged PRs (30d)
24

Description

Parent

  • #4005

Context

The framework-neutral renderer from the previous issue defines the HTML and SSR contract for Shopify's account widget. React and Vue users should receive idiomatic bindings that hide Shopify's element hierarchy, slot assignment, event wiring and CSP-safe layout styles.

The <shopify-account> Storefront Web Component renders a customer-account avatar. When selected, it opens Shopify's account sheet with sign-in options or signed-in customer links.

The signed-out avatar must be explicit and required. Broad React children and Vue default-slot APIs make its purpose unclear and allow an empty SSR fallback by accident.

Proposed APIs

React uses a required prop:

<ShopifyAccountWidget
  storeDomain={storeDomain}
  publicAccessToken={publicAccessToken}
  customerAccessToken={customerAccessToken}
  signedOutAvatar={
    <img src="/account.svg" width={44} height={44} alt="" />
  }
  onOpen={handleOpen}
  onClose={handleClose}
/>

Vue uses an explicit required named slot rather than its broad default slot:

<ShopifyAccountWidget
  :store-domain="storeDomain"
  :public-access-token="publicAccessToken"
  :customer-access-token="customerAccessToken"
  @open="handleOpen"
  @close="handleClose"
>
  <template #signed-out-avatar>
    <img src="/account.svg" width="44" height="44" alt="" />
  </template>
</ShopifyAccountWidget>

Both bindings should render the supplied content inside one Hydrogen-owned wrapper with slot="signed-out-avatar". Consumers must not add the native slot attribute themselves.

Event behaviour

Shopify dispatches open and close as non-bubbling CustomEvents from <shopify-account>.

  • React must attach listeners directly with addEventListener() so the binding works with both React 18 and React 19.
  • Vue must listen on the underlying <shopify-account> and re-emit framework events from the wrapper component.
  • Listeners must be updated and removed when handlers or component lifecycles change.

Acceptance criteria

  • Export ShopifyAccountWidget and its prop types from @shopify/hydrogen/react.
  • Export the equivalent component and types from @shopify/hydrogen/vue.
  • React requires signedOutAvatar: ReactElement; it does not use children for the avatar.
  • Vue requires an explicit signed-out-avatar named slot; it does not use the default slot.
  • Both bindings add slot="signed-out-avatar" through a stable Hydrogen-owned wrapper.
  • The bindings render the shared core structure and nonce-bearing stylesheet without copying attribute, default or CSS knowledge.
  • The bindings do not require className, user CSS or inline style attributes for layout stability.
  • Documentation requires visual-only avatar content. It must not include links, buttons or other focusable elements because Shopify projects it inside its own button.
  • React exposes onOpen and onClose and listens directly on the custom element using native event listeners.
  • Vue exposes open and close events and listens directly on the custom element.
  • Event tests verify the documented event object, listener replacement and cleanup.
  • Changing, adding or removing customerAccessToken causes the widget to reflect the new signed-in state. If Shopify does not observe the store attribute, deliberately remount the custom element and test that behaviour.
  • SSR and hydration complete without mismatches, including when a CSP nonce is present.
  • Tests cover required-avatar typing, avatar placement, events, hydration and customer-token changes.
  • Replace the React Router template's raw renderShopifyAccountWidget() integration with the React binding without changing its rendered behaviour.
  • Update the Customer Account skill and public documentation with core, React and Vue examples.
  • Documentation explains that ShopifyScripts accountWidget is still required.
  • Add a minor changeset.

Out of scope

  • Broad React children or Vue default-slot content.
  • Requiring consumers to provide slot="signed-out-avatar".
  • Interactive content inside the signed-out avatar.
  • Styling or replacing Shopify's account sheet internals.
  • Implementing authentication or account pages.

Blocked by

  • #4007

References

  • <shopify-account> events and slots
  • packages/hydrogen/src/react/shop-pay.tsx
  • packages/hydrogen/src/vue/shop-pay.ts
  • packages/hydrogen/skills/hydrogen-customer-account/SKILL.md

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

This work is blocked by #4007 and builds on the framework-neutral renderer from #4005. Start by reading packages/hydrogen/src/react/shop-pay.tsx, packages/hydrogen/src/vue/shop-pay.ts, and packages/hydrogen/skills/hydrogen-customer-account/SKILL.md, then locate the React Router template integration. Done means both bindings, exports, event and token-change tests, SSR/hydration coverage, documentation, the template migration, and a minor changeset are complete.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
documentation, frontend, testing
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.