Integration and customisation guides for the customer-facing portal options provided by Submarine.

Classic Customer Accounts

While many merchants are adopting Shopify’s new customer account system, there are still millions of merchants using the classic (or “legacy”) customer account experience, and it’s still a common choice for merchants where high levels of customisation or styling are required, or where existing applications or requirements can’t be integrated into new customer accounts.
Submarine provides an out-of-the-box customer-facing portal that leverages Shopify’s existing customer authentication system (so if you’re logged in to your Shopify account, you’re logged in to your Submarine portal) and renders within the context of your existing Shopify theme to provide a native feel.

Integrating with classic customer accounts

The easiest way to integrate Submarine’s out-of-the-box customer portal is to create a link to the portal pages within the main /account page. The default URLs for each of Submarine’s portal surfaces are:
  • https://store.myshopify.com/apps/platform/portal/subscriptions
  • https://store.myshopify.com/apps/platform/portal/memberships
  • https://store.myshopify.com/apps/platform/portal/presales
  • https://store.myshopify.com/apps/platform/portal/crowdfunding
When these links are followed, Submarine will render a Liquid content page using your theme’s default layout and load the customer portal React app.
The out-of-the-box Submarine customer portal for subscription management in Classic Customer Accounts.
The out-of-the-box Submarine customer portal for subscription management in Classic Customer Accounts.

Customising classic customer accounts

While the out-of-the-box customer portal comes with default styling that should work well with many Shopify themes, especially those built on the Dawn framework, it’s inevitable that merchants and developers will need to tweak the presentation of the portal to align with the rest of the site’s branding and experience.
Rather than provide an endless array of style configuration options (which often leads to an “uncanny valley” of almost-but-not-quite style matching), Submarine provides a couple of configuration mechanisms to allow the utilisation of existing styles and classes wherever possible.
These configuration mechanisms are driven by the setting of shop-level configuration metafields, so they’re currently only exposed to developers familiar with Shopify’s metafield API. This will be made more accessible in the long term.
There are two metafields that can be set to control the behaviour and appearance of the out-of-the-box customer portal, both typed as JSON objects — their purpose and structure is described below.

Porthole configuration metafield

The first metafield, shop.submarine.porthole_configuration, contains high-level configuration options for the portal, and looks like this:
json
{ "js_uri": "https://cdn.submarineplatform.com/@submarine/porthole-react-classic/0.0.11/porthole-react-classic.js", "css_uri": "https://cdn.shopify.com/shops/348957398475/styles.css", "layout": "theme", "theme": "dawn" }
  • js_uri defines the URL to load the Porthole React application from. This is set to the latest version of the portal by default, and should only be overridden if you’re building your own specific fork of the portal (see “Forking classic customer accounts” below).
  • css_uri is blank by default, but if provided it will load the provided URL as a stylesheet into the page. This can be used to load styles that aren’t incorporated in your theme layout by default, or to specific a specific CSS file with overrides targeting the portal.
  • layout defines the name of the theme layout to use when rendering the portal page. Defaults to theme , but can also be set to none.
  • theme allows you to specify a preset base theme. Currently, only default or dawn are provided (you can view the list and details here).

Porthole theme metafield

The second metafield, shop.submarine.porthole_theme, can be used to set CSS classes or styles at the level of individual components, and could look something like this:
json
{ "Button": { "classes": { "base": "button-outlined text-base tracking-default", "loading": "loading", "size": { "micro": "button--small" }, "variant": { "primary": "button-primary", "secondary": "button-outlined", "tertiary": "" } }, "styles": { "base": { "textColor": "white" }, "loading": { "textColor": "orange" } } }, "PageHeader": { "classes": { "base": "header relative flex items-center justify-between rounded bg-brand-marine px-4 py-3 lg:mb-4 lg:px-0 lg:py-0 lg:bg-transparent" }, "styles": { "base": { "backgroundColor": "white" } } }, "Text": { "classes": { "as": { "h1": "text-xl font-medium tracking-normal text-white lg:text-grey-900 lg:text-2xl", "h2": "text-lg font-medium tracking-normal text-white lg:text-grey-900 lg:text-xl" } } } }
The structure of this metafield allows you to specify particular CSS classes or styles that should be applied against individual React components rendered as part of the portal. This allows really fine-grained control over the appearance of components and the reuse of existing classes and styles, rather than having to redefine and match styles exactly.
Let’s break down the structure of a component style definition for the Button component:
json
{ "Button": { "classes": { "base": "button-outlined text-base tracking-default", "loading": "loading", "size": { "micro": "button--small" }, "variant": { "primary": "button-primary", "secondary": "button-outlined", "tertiary": "" } }, "styles": { "base": { "textColor": "white" }, "loading": { "textColor": "orange" } } } }
A component definition has two top-level keys, classes and styles. As their names suggest, classes allows you to define a list of CSS class names that should be applied to the component under certain circumstances, and styles does the same with individual CSS style properties.
For both classes and styles, anything defined in base will be applied to the component at all times (so for example, button-outlined text-base tracking-default in our example). You can then define additional classes or styles when particular modifiers (props) are applied to a component — these could either be boolean properties (like loading in the example above) or an enumerable or dynamic property value like size or variant.
For the full list of style-able components and the properties and variants they receive, you can check out the components directory on Github. Any component that uses the themedComponent hook will be customisable using this technique.

Setting classic customer account configuration metafields

As shop-level metafields aren’t easily manageable within the Shopify admin, here’s an example of how you can use a cURL request to set both of the Porthole metafields on your store.
<API_TOKEN> should be replaced with a Shopify API access token that has metafield write permissions, and <SHOP_ID> should be replaced with the Shopify integer ID of your store (which can be found at https://admin.shopify.com/store/store/shop.json).
bash
curl -X "POST" "https://store.myshopify.com/admin/api/2024-10/graphql.json" \ -H 'Accept: application/json' \ -H 'Content-Type: application/json' \ -H 'X-Shopify-Access-Token: <API_TOKEN>' \ -d $'{ "query": "mutation metafieldsSet($metafields:[MetafieldsSetInput!]!){metafieldsSet(metafields:$metafields){metafields{id}userErrors{field message}}}", "variables": { "metafields": [ { "value": "{ \\"js_uri\\": \\"https://cdn.submarineplatform.com/@submarine/porthole-react-classic/0.0.11/porthole-react-classic.js\\", \\"css_uri\\": \\"https://cdn.shopify.com/shops/348957398475/styles.css\\", \\"layout\\": \\"theme\\", \\"theme\\": \\"dawn\\" }", "key": "porthole_configuration", "namespace": "submarine", "ownerId": "gid://shopify/Shop/<SHOP_ID>", "type": "json" }, { "value": "{ \\"Button\\": { \\"classes\\": { \\"base\\": \\"button-outlined text-base tracking-default\\", \\"loading\\": \\"loading\\", \\"size\\": { \\"micro\\": \\"button--small\\" }, \\"variant\\": { \\"primary\\": \\"button-primary\\", \\"secondary\\": \\"button-outlined\\", \\"tertiary\\": \\"\\" } } }, \\"PageHeader\\": { \\"classes\\": { \\"base\\": \\"header relative flex items-center justify-between rounded bg-brand-marine px-4 py-3 lg:mb-4 lg:px-0 lg:py-0 lg:bg-transparent\\" } }, \\"Text\\": { \\"classes\\": { \\"as\\": { \\"h1\\": \\"text-xl font-medium tracking-normal text-white lg:text-grey-900 lg:text-2xl\\", \\"h2\\": \\"text-lg font-medium tracking-normal text-white lg:text-grey-900 lg:text-xl\\" } } } }", "key": "porthole_theme", "namespace": "submarine", "ownerId": "gid://shopify/Shop/<SHOP_ID>", "type": "json" } ] } }'

Forking classic customer accounts

Of course, if you want to have more fine-grained control over the presentation and functionality of your customer portal without having to start from scratch, you can fork the open source codebase for Porthole’s classic customer account (the app here), compile it with yarn build, and update the js_uri attribute in the Porthole configuration metafield to point to your custom build of the React app.

New Customer Accounts

Component support for Shopify’s new customer accounts and customer account extensions is being added to the Porthole Framework.
Contact us if you’re interested in being an early adopter!