> ## Documentation Index
> Fetch the complete documentation index at: https://swish.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Hydrogen

> Add wishlists and saved items to a Shopify Hydrogen storefront with the Swish React SDK.

Swish integrates into Hydrogen via `@swishapp/react` for the UI layer and `@swishapp/auth-proxy` for secure token creation on the server.

> [!NOTE]
>
> Before you start, make sure you have:
>
> * Swish installed from the Shopify App Store
> * A Hydrogen project running locally
> * A Swish API token (contact [support@swish.app](mailto:support@swish.app) to request one)
> * A Shopify Storefront API access token (see [configuration reference](https://swish.app/docs/references/sdk/configuration.md#storefront) for how to create one)

## Install packages

```bash npm theme={null}
npm install @swishapp/react @swishapp/auth-proxy
```

```bash pnpm theme={null}
pnpm add @swishapp/react @swishapp/auth-proxy
```

```bash yarn theme={null}
yarn add @swishapp/react @swishapp/auth-proxy
```

## Set up the auth proxy

Create a server route that securely creates Swish API tokens. The auth proxy validates the request and forwards it to the Swish API using your secret token.

```ts app/routes/api.swish-token.ts theme={null}
import type { ActionFunctionArgs } from "react-router";
import { createAuthProxy } from "@swishapp/auth-proxy";

const proxy = createAuthProxy({
  authToken: process.env.SWISH_API_TOKEN,
  authenticate: async (request, { context }) => {
    const customerAccessToken = await context.session.get("customerAccessToken");
    return customerAccessToken?.customerId ?? null;
  },
  onError: (error) => console.error("Swish auth proxy error:", error),
});

export async function action({ request, context }: ActionFunctionArgs) {
  return proxy.createToken(request, { context });
}
```

The `authenticate` callback returns the Shopify customer ID for logged-in customers, or `null` for guests. See the [auth proxy reference](https://swish.app/docs/guides/headless/auth-proxy.md) for all configuration options.

## Add SwishProvider

Wrap your app in `SwishProvider` so all child components can use Swish hooks. A good place is a layout route that has access to customer and localization data from your Hydrogen loader.

```tsx app/components/SwishSetup.tsx theme={null}
import { SwishProvider } from "@swishapp/react";
import type { ReactNode } from "react";

interface SwishSetupProps {
  children: ReactNode;
  customer: {
    id: string | null;
    email: string | null;
    firstName: string | null;
    lastName: string | null;
  };
  localization: {
    country: string;
    language: string;
  };
}

export function SwishSetup({ children, customer, localization }: SwishSetupProps) {
  return (
    <SwishProvider
      options={{
        auth: {
          tokenUrl: "/api/swish-token",
        },
        storefront: {
          storeDomain: import.meta.env.VITE_STORE_DOMAIN,
          accessToken: import.meta.env.VITE_STOREFRONT_API_TOKEN,
        },
        context: {
          customer: {
            id: customer.id,
            email: customer.email,
            firstName: customer.firstName,
            lastName: customer.lastName,
          },
          localization: {
            country: localization.country,
            language: localization.language,
            market: localization.market,
          },
          myshopifyDomain: import.meta.env.VITE_STORE_DOMAIN,
          shopName: "My Store",
          newCustomerAccounts: true,
          routes: { accountUrl: "/account" },
        },
      }}
    >
      {children}
    </SwishProvider>
  );
}
```

Then use it in your root layout:

```tsx app/root.tsx theme={null}
import { SwishSetup } from "~/components/SwishSetup";

export default function App() {
  const { customer, localization } = useLoaderData<typeof loader>();

  return (
    <SwishSetup customer={customer} localization={localization}>
      <Outlet />
    </SwishSetup>
  );
}
```

> [!NOTE]
>
> The SDK is client-only. `SwishProvider` renders safely during SSR, but the SDK initializes after hydration. Hooks return loading states until then.

See the [SwishProvider reference](https://swish.app/docs/references/react/provider.md) for all available options and the [configuration reference](https://swish.app/docs/references/sdk/configuration.md) for the full `options` shape.

## Add a save button

Use `useSwishItem` on a product detail page to let customers save and unsave products.

```tsx app/components/SaveButton.tsx theme={null}
import { useSwishItem } from "@swishapp/react";

export function SaveButton({ productId }: { productId: string }) {
  const { saved, toggle, loading } = useSwishItem({ productId });

  if (loading) return null;

  return (
    <button type="button" onClick={toggle}>
      {saved ? "Saved" : "Save"}
    </button>
  );
}
```

Use it on your product detail page with the product ID from the loader:

```tsx theme={null}
<SaveButton productId={product.id} />
```

## Add an item count badge

Use `useSwishItemCount` to show how many items a customer has saved, for example in the site header.

```tsx app/components/SwishBadge.tsx theme={null}
import { useSwishItemCount } from "@swishapp/react";

export function SwishBadge() {
  const { count } = useSwishItemCount();

  if (count === 0) return null;

  return <span className="badge">{count}</span>;
}
```

## Sample

A more complete example showing a product detail page with a save button and the item count badge in the header.

```tsx app/routes/products.$handle.tsx theme={null}
import { useLoaderData } from "react-router";
import { SaveButton } from "~/components/SaveButton";

export default function ProductPage() {
  const { product } = useLoaderData<typeof loader>();

  return (
    <div>
      <h1>{product.title}</h1>
      <img
        src={product.featuredImage?.url}
        alt={product.featuredImage?.altText ?? product.title}
      />
      <p>{product.description}</p>
      <SaveButton productId={product.id} />
    </div>
  );
}
```

```tsx app/components/Header.tsx theme={null}
import { SwishBadge } from "~/components/SwishBadge";

export function Header() {
  return (
    <header>
      <nav>{/* navigation links */}</nav>
      <button type="button" swish-intent="view:home">
        Wishlist <SwishBadge />
      </button>
    </header>
  );
}
```

The `swish-intent="view:home"` attribute opens the Swish drawer when clicked, no JavaScript handler needed. See the [intents reference](https://swish.app/docs/references/sdk/intents.md) for all available intents.

## Next steps

> [card] [Customization](https://swish.app/docs/guides/customize/overview.md)
>
> Design tokens, translations, and component configuration.

> [card] [Intents](https://swish.app/docs/references/sdk/intents.md)
>
> Trigger actions like save, unsave, open drawer, and quick buy.

> [card] [React hooks](https://swish.app/docs/references/react/hooks.md)
>
> All available hooks for items, lists, counts, and queries.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.