> ## 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.

# Next.js

> Add wishlists and saved items to a Next.js storefront with the Swish React SDK.

Swish integrates into Next.js 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 Next.js project running locally (App Router)
> * 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 Route Handler 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/api/swish-token/route.ts theme={null}
import { createAuthProxy } from "@swishapp/auth-proxy";
import { cookies } from "next/headers";

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

export async function POST(request: Request) {
  return proxy.createToken(request);
}
```

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

## Add SwishProvider

`SwishProvider` is a client component. Create a wrapper that accepts customer and localization data as props from a server component.

```tsx components/swish-setup.tsx theme={null}
"use client";

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;
    tags?: string[] | null;
  };
  localization: {
    country: string;
    language: string;
  };
}

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

`customer.tags` is optional. Pass it to target [content blocks](https://swish.app/docs/guides/customize/views/home.md#content-block) at tagged customers; omit it and those blocks simply never match.

Then use it in your root layout. The layout is a server component, so you can fetch customer and localization data before passing them down.

```tsx app/layout.tsx theme={null}
import { SwishSetup } from "@/components/swish-setup";

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const customer = await getCustomer();
  const localization = await getLocalization();

  return (
    <html>
      <body>
        <SwishSetup customer={customer} localization={localization}>
          {children}
        </SwishSetup>
      </body>
    </html>
  );
}
```

> [!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. Hooks require a client component.

```tsx components/save-button.tsx theme={null}
"use client";

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 server component:

```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 components/swish-badge.tsx theme={null}
"use client";

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/products/[handle]/page.tsx theme={null}
import { SaveButton } from "@/components/save-button";

export default async function ProductPage({ params }: { params: { handle: string } }) {
  const product = await getProduct(params.handle);

  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 components/header.tsx theme={null}
import { SwishBadge } from "@/components/swish-badge";

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.