npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@nosto/shopify-hydrogen

v2.2.4

Published

Component library to implement Nosto within Shopify Hydrogen

Downloads

38

Readme

@nosto/shopify-hydrogen

Welcome to the Nosto React Component Library for Shopify Hydrogen!

Our component library is designed to help you easily integrate Nosto features into your Hydrogen-based storefront.

The library includes a comprehensive set of reusable components, each designed to support a specific feature or functionality of Nosto. With our library, you can quickly and easily implement Nosto features into your storefront.

This README is designed to provide you with an overview of our component library, including instructions on how to install and use our components, as well as information on the features and functionalities that our library supports.

:warning: Please note that the information provided in this documentation is specific to Hydrogen 2, which is built on Remix. If your storefront is still running on Hydrogen 1, we recommend referring to our documentation specifically tailored for Hydrogen 1. This will ensure that you access the appropriate guidance and instructions for your specific version.

nosto-react

It's important to note that our React component library for Shopify Hydrogen is an extension of our nosto-react library, a powerful and flexible library that provides seamless integration with Nosto. Our Shopify Hydrogen-specific component library builds upon the core functionality of nosto-react, adding Hydrogen-specific hooks and logic to make integration even easier and more intuitive.

Demo store

To see our React component library in action in a Hydrogen project, we invite you to check out our Shopify Hydrogen demo store. The code for the demo store is available on our GitHub repository. We hope that our Hydrogen demo store will serve as a source of inspiration for your own storefront development, and we encourage you to explore our component library as well and customize it further to fit your unique needs.

Feature list

Our React component library for Shopify Hydrogen includes the following features:

  • Recommendations, including client-side rendering
  • Onsite content personalisation
  • Dynamic bundles
  • Debug toolbar* (excluding advanced use cases)
  • Pop-ups & personalised emails
  • A/B testing
  • Segmentation and Insights
  • Analytics
  • Search** (when implemented via our code editor)
  • Shopify Markets

*Note: Our React component library currently does not support advanced use cases of the debug toolbar, but we are constantly working to improve our library and provide you with the best possible integration options.

**Note: The search feature is available when implemented via our code editor.

Installation

To install the package into your project, run:

npm install @nosto/shopify-hydrogen

Or if you prefer using Yarn:

yarn add @nosto/shopify-hydrogen

Usage

Adding Nosto's fetcher function to the root loader

  • Import getNostoData from @nosto/shopify-hydrogen into your root.jsx file
  • Invoke the function, passing an object that contains both context and cartId as properties
  • Spread it to the returned defer object of the root loader by using the spread operator ... and await keyword: ...(await getNostoData({ context, cartId }))

By following these steps, you enable Nosto components to seamlessly receive relevant server-based data through the root loader.

// app/root.jsx

import { getNostoData } from '@nosto/shopify-hydrogen'

export async function loader({ request, context }) {
  const cartId = getCartId(request);
  //...

  return defer({
    ...(await getNostoData({ context, cartId })),
  });
}

Adding components

The library uses @nosto/nosto-react under the hood combined with Hydrogen functionality. You can import the following components:

NostoProvider

  • The NostoProvider component is required and provides the Nosto functionality.
  • It must wrap all other Nosto components.
  • Pass your Nosto merchant ID via the account prop.
  • Pass Shopify Hydrogen nonce as a nonce prop. This is used to add your stores content security policy nonce to a Nosto script.
  • Imports the Nosto client script into the window environment. This is used to control all of Nosto functionality.
  • Remix separates the App and ErrorBoundary within the root. Make sure to add to both for also enabling Nosto on 404 pages.
  • The currentVariation prop is automatically detected and managed within the NostoProvider component. However, if you prefer to set it manually, you can simply pass the prop directly yourself.
// app/root.jsx

import { NostoProvider } from "@nosto/shopify-hydrogen";

export default function App() {
  const nonce = useNonce();
  const data = useLoaderData();
  
  return (
      ...
      <body>
        <NostoProvider account="shopify-11368366139" recommendationComponent={<NostoSlot />} nonce={nonce}>
          <Layout>
            <Outlet/>
          </Layout>
        </NostoProvider>
        <ScrollRestoration />
        <Scripts />
      </body>
  );
}

export function ErrorBoundary() {

  const [root] = useMatches();

  return (
      ...
      <body>
        <NostoProvider account="shopify-11368366139" recommendationComponent={<NostoSlot />}>
          <Layout/>
        </NostoProvider>
        <Scripts />
      </body>
  );
}

Shopify Markets

  • Make sure you have configured markets within your Shopify account. If you need assistance with the setup process, we recommend referring to this helpful article.
  • To enable Shopify Markets for Nosto in Hydrogen, you can utilize the shopifyMarkets prop when using the <NostoProvider/> component.
  • You can enable automatic market and language detection by simply passing true as the value for the shopifyMarkets prop.
  • Alternatively, you can manually pass the language and market ID individually using the following format: {language: "EN", marketId: "123456789"}.
// Enable with automatic market and language detection:
<NostoProvider shopifyMarkets={true} account="shopify-11368366139" nonce={nonce}/>

// Manually set only the language of the market:
<NostoProvider shopifyMarkets={{ language: "EN" }} account="shopify-11368366139" nonce={nonce}/>

// Manually set both the language and ID of the market:
<NostoProvider shopifyMarkets={{ language: "EN", marketId: '123456789' }} account="shopify-11368366139" nonce={nonce}/>

Client side rendering for recommendations

In order to implement client-side rendering, the requires a designated component to render the recommendations provided by Nosto. This component should be capable of processing the JSON response received from our backend. Notice the recommendationComponent={<NostoSlot />} prop passed to <NostoProvider> above.

We have included a set of basic components as examples, however, additional customizations can be made to suit specific requirements. It is important to note that these components serve as a starting point for implementation.

// app/components/nosto/NostoItem.jsx

export function NostoItem({product, onClick}) {
  return (
    <div className="nosto-item" onClick={onClick}>
      <a href={product.url}>
        <div className="nosto-image-container">
          <div className="nosto-image">
            <img src={product.thumb_url} alt={product.name} />
          </div>
          <div className="nosto-product-details">
            <div className="nosto-product-name">{product.name}</div>
            <div className="nosto-product-price">{product.price_text}</div>
          </div>
        </div>
      </a>
    </div>
  );
}

Additionally, we have a component that will use <NostoItem> in slots defined for recommendations.

// app/components/nosto/NostoSlot.jsx

import { NostoItem } from './NostoItem';

export function NostoSlot({nostoRecommendation}) {
  let {title, products, result_id} = nostoRecommendation;

  function reportClick(productId) {
    // To report attribution for product clicks towards segmentation & analytics
    window?.nostojs(function (api) {
      api.defaultSession().recordAttribution('vp', productId, result_id).done();
    });
  }

  return (
    <div className="nosto-container">
      <h2 className="nosto-title">{title}</h2>
      <div className="nosto-list">
        {products.map((product) => (
          <NostoItem
            product={product}
            key={product.product_id}
            onClick={() => {
              reportClick(product.product_id);
            }}
          />
        ))}
      </div>
    </div>
  );
}

NostoSession

In Hydrogen the NostoSession component is rendered automatically, no need for manually adding it to your app.

NostoPlacement

  • The NostoPlacement component renders a static Nosto placement.
  • Placements are used to inject recommendations and content directly into the page. It’s advised to add these wherever you may wish to add personalisation later.
  • Pass the placement id via the id prop.
import { NostoPlacement } from "@nosto/shopify-hydrogen";

<NostoPlacement id="frontpage-nosto-1" />;
<NostoPlacement id="frontpage-nosto-2” />;
:warning: Dynamic placements and Shopify Hydrogen

Please note that the concept of dynamic placements does not apply to Shopify Hydrogen headless environments, as they can interfere with React's DOM rendering process and adversely affect site navigation. As such, we have disabled Nosto's dynamic placement feature in our React component library for Shopify Hydrogen builds. Instead, all placements should be statically placed where needed with the NostoPlacement component described above. While dynamic placements may be a useful feature in other environments, we have found that they are not compatible with the unique architecture of Shopify Hydrogen, and can cause unexpected behavior in your storefront.

Optionally passing placement IDs to page type components

An optional prop called "placements" can be passed for all components in the library. This prop is a string array that accepts placement IDs. When used, it allows Nosto to render only the placements you specify, rather than all placements on the page (which is the default behavior). This can include placements that are 'visible' on the page, like ones in the background such as those on a product detail page or your homepage when your mini-cart is open. To use this prop, simply pass the placement IDs you wish to render as an array of strings. For example, if you only want to render placements "frontpage-nosto-1" and "frontpage-nosto-2" you would pass the following prop to the <NostoHome> component:

<NostoHome placements={['frontpage-nosto-1', 'frontpage-nosto-2']} />

NostoHome

  • The NostoHome component needs to be added on the home/front page.
  • It loads the campaigns for all the Nosto placements on the page.
  • No props required.
  • Must be added at the end of all Nosto components on the page.
  • This is a page-specific tag. There are other page-specific components described later in this doc.
// app/routes/($locale)._index.jsx

import { NostoHome, NostoPlacement } from "@nosto/shopify-hydrogen";

function Homepage() {
  return (
    <>
      ...
      <NostoPlacement id="frontpage-nosto-1" />
      <NostoPlacement id="frontpage-nosto-2" />
      <NostoHome />
    </>
  );
}

NostoProduct

  • The NostoProduct component needs to be added on product pages.
  • It loads the campaigns for all the <NostoPlacement>s on the page.
  • Pass the product ID via the product prop
  • For Nosto tagging, pass the product data via the tagging prop. This allows Nosto to better personalise the result served to the page.
  • Must be added at the end of all Nosto components on the page.
// app/routes/($locale).products.$productHandle.jsx

import { NostoProduct, NostoPlacement } from '@nosto/shopify-hydrogen';

export default function Product() {

  ...
  const { product } = useLoaderData();

  let nostoProductId = product?.id?.split('/');
  nostoProductId && (nostoProductId = nostoProductId[nostoProductId.length - 1]);

  return (
      <>
        ...
        <NostoPlacement id="productpage-nosto-1" />
        <NostoPlacement id="productpage-nosto-2" />
        <NostoProduct product={nostoProductId} tagging={product} />        
      </>
  );
}

NostoCategory

  • The NostoCategory component needs to be added on collection pages.
  • It loads the campaigns for all the Nosto placements on the page.
  • Pass the collection title via the category prop.
  • Must be added at the end of all Nosto components on the page.
// app/routes/($locale).collections.$collectionHandle.jsx

import { NostoCategory, NostoPlacement } from "@nosto/shopify-hydrogen";

export default function Collection() {

  const { collection } = useLoaderData();

  return (
    <>
      ...
      <NostoPlacement id="categorypage-nosto-1" />
      <NostoPlacement id="categorypage-nosto-2" />
      <NostoCategory category={collection.title} />
    </>
  );
}

NostoSearch

  • The NostoSearch component needs to be added on the search page.
  • It loads the campaigns for all the Nosto placements on the page.
  • Pass the search term via the query prop.
  • Must be added at the end of all Nosto components on the page.
// app/routes/($locale).search.jsx

import { NostoSearch, NostoPlacement } from "@nosto/shopify-hydrogen";

export default function Search() {

  const { searchTerm } = useLoaderData();  

  return (
    <>
      ...
      <NostoPlacement id="searchpage-nosto-1" />
      <NostoPlacement id="searchpage-nosto-2" />
      <NostoSearch query={searchTerm ? decodeURI(searchTerm) : ""} />
    <>
  );
}

NostoOther

  • The NostoOther component needs to be added on pages with no specific page type.
  • It loads the campaigns for all the Nosto placements on the page.
  • No props required.
  • Must be added at the end of all Nosto components on the page.
import { NostoOther, NostoPlacement } from "@nosto/shopify-hydrogen";

function OtherPage() {
  return (
    <>
      ...
      <NostoPlacement id="other-nosto-1" />
      <NostoPlacement id="other-nosto-2" />
      <NostoOther />
    </>
  );
}

NostoCheckout

  • The NostoCheckout component needs to be added on the cart page.
  • It loads the campaigns for all the Nosto placements on the page.
  • No props required.
  • Must be added at the end of all Nosto components on the page.
  • Order details are automatically passed with the help of the Nosto script that is loaded on the page already. Once the user is taken back to the website post-payment, this order information is logged internally.
// app/components/Cart.jsx

import { NostoCheckout, NostoPlacement } from "@nosto/shopify-hydrogen";

export function Cart({ layout, onClose, cart }) {

  const linesCount = Boolean(cart?.lines?.edges?.length || 0);

  return (
    <>
      <CartEmpty hidden={linesCount} onClose={onClose} layout={layout} />
      <CartDetails cart={cart} layout={layout} />

      {/* Render specific Nosto slot when there are items in cart: */}
      {linesCount && (<NostoPlacement id="cartpage-nosto-1" />)}

      <NostoCheckout />
    </>
  );
}

export function CartEmpty({ hidden = false}) {

  return (
    <div>
      ...

      {/* Render specific Nosto slot when cart is empty: */}
      {!hidden && (<NostoPlacement id="cartpage-nosto-2" />)}

    </div>
  );
}

Nosto404

  • The Nosto404 component needs to be added on 404/not found pages.
  • It loads the campaigns for all the Nosto placements on the page.
  • No props required.
  • Must be added at the end of all Nosto components on the page.
  • Make sure to wrap NostoProvider around ErrorBoundary inside root.jsx
// app/components/NotFound.jsx

import { Nosto404, NostoPlacement } from "@nosto/shopify-hydrogen";

export function NotFound() {
  return (
    <>
      ...
      <NostoPlacement id="notfound-nosto-1" />
      <NostoPlacement id="notfound-nosto-2" />
      <Nosto404 />
    </>
  );
}

Feedback

If you've found a feature missing or you would like to report an issue, simply open up an issue and let us know.

We're always collecting feedback and learning from your use-cases. If you find yourself customising widgets and forking the repo to make patches - do drop a message. We'd love to know more and understand how we can make this library even better for you.

Authors

See also the list of contributors who participated in this project.

License

MIT License © Nosto Solutions