> For the complete documentation index, see [llms.txt](https://docs.maker.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maker.co/maker-greatstore/get-started/installation/react.md).

# Custom React

Add GreatStore to a custom React storefront built with Next.js or Vite.

For a storefront you build yourself in React, use the [`@greatstore/react`](/maker-greatstore/developer-reference/react.md) component rather than a script tag. It loads the chat when your app mounts and cleans up after itself, so it behaves well with client-side navigation.

This guide is for **React 18 or 19**, and covers the three most common setups:

* **Next.js App Router** (Next.js 13.4 and later, with an `app/` folder)
* **Next.js Pages Router** (any Next.js version, with a `pages/` folder)
* **Vite**, and other client-rendered React apps

## Install the package

```bash
npm install @greatstore/react
```

`store` is your store's address: the `{your-address}` part of `{your-address}.greatstore.ai`.

## Share the chat across your app

A chat button in the header and an "Ask about this product" button on a product page both need to reach the same chat. The simplest way is one small provider that renders the chat once and hands its controls to any component. It's the same in every setup; only where you mount it differs.

```tsx
// greatstore-provider.tsx
"use client";

import { GreatStore, type GreatStoreHandle } from "@greatstore/react";
import { createContext, useContext, useRef, type ReactNode, type RefObject } from "react";

const ChatContext = createContext<RefObject<GreatStoreHandle | null> | null>(null);

export function GreatStoreProvider({ children }: { children: ReactNode }) {
  const chat = useRef<GreatStoreHandle>(null);
  return (
    <ChatContext.Provider value={chat}>
      {children}
      <GreatStore ref={chat} store="{your-address}" />
    </ChatContext.Provider>
  );
}

export function useChat() {
  const chat = useContext(ChatContext);
  if (!chat) throw new Error("useChat must be used inside GreatStoreProvider");
  return chat;
}
```

The `"use client"` line is required by the Next.js App Router and harmless everywhere else.

## Mount the provider

### Next.js App Router

Wrap your app in the root layout, `app/layout.tsx`:

```tsx
import { GreatStoreProvider } from "./greatstore-provider";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <GreatStoreProvider>{children}</GreatStoreProvider>
      </body>
    </html>
  );
}
```

The layout stays a Server Component; only the provider runs in the browser. Because the root layout persists across navigations, the chat stays open while shoppers move between pages.

### Next.js Pages Router

Wrap your app in `pages/_app.tsx`:

```tsx
import type { AppProps } from "next/app";
import { GreatStoreProvider } from "../greatstore-provider";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <GreatStoreProvider>
      <Component {...pageProps} />
    </GreatStoreProvider>
  );
}
```

### Vite

Wrap your root component in `src/main.tsx`:

```tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import { GreatStoreProvider } from "./greatstore-provider";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <GreatStoreProvider>
      <App />
    </GreatStoreProvider>
  </StrictMode>,
);
```

If you use React Router, keep the provider **outside** your routes, as here, so the chat isn't reloaded on every page change.

## Add buttons

Any component inside the provider can open the chat or ask a question for the shopper:

```tsx
"use client";

import { useChat } from "./greatstore-provider";

export function ChatButton() {
  const chat = useChat();
  return (
    <button type="button" onClick={() => chat.current?.open()}>
      Chat with us
    </button>
  );
}

export function AskAboutProduct({ name }: { name: string }) {
  const chat = useChat();
  return (
    <button type="button" onClick={() => chat.current?.sendMessage(`Tell me more about ${name}`)}>
      Ask about this product
    </button>
  );
}
```

## Tell the assistant what the shopper is viewing

On a product page, pass the product details whenever they change, so a shopper can ask "does this come in blue?" without naming it. Calls made before the chat has finished loading are held and applied once it's ready.

```tsx
"use client";

import { useEffect } from "react";
import { useChat } from "./greatstore-provider";

export function ProductChatContext({ product, variant }: { product: Product; variant: Variant }) {
  const chat = useChat();
  useEffect(() => {
    chat.current?.updateModelContext(
      `Viewing: ${product.title}, ${variant.title}, ${variant.price}, ${variant.available ? "in stock" : "sold out"}.`,
    );
  }, [chat, product, variant]);
  return null;
}
```

Render `<ProductChatContext>` on your product page with your own product and variant types. Each call replaces the last, so always send the full current state. Everything else the chat can do (styling props, structured content, notifications) is in the [React SDK](/maker-greatstore/developer-reference/react.md) and [JavaScript API](/maker-greatstore/developer-reference/javascript-api.md) references.

## Developing locally

The chat works on the **Website** address set under **Configure → Basic**. Your local dev server (`http://localhost:3000` for Next.js, `http://localhost:5173` for Vite) and preview deployments (such as Vercel or Netlify preview URLs) are separate addresses, so add each one you use under [Embed origins](/maker-greatstore/configure/security.md#embed-origins).

## Troubleshooting

<details>

<summary>The button does nothing</summary>

Open your browser's developer console. If you see **"Chat is unavailable on …"**, add the page's address under [Embed origins](/maker-greatstore/configure/security.md#embed-origins). To be told about loading problems in code, pass an `onError` handler to `<GreatStore>`.

</details>

<details>

<summary>Only one chat works when I render the component twice</summary>

A page can hold one chat. Render `<GreatStore>` once, near the root, as the provider above does, and reach it from anywhere through `useChat`.

</details>

<details>

<summary>Changing a prop after the page loads has no effect</summary>

Props are read once, when the chat first loads. Set them at mount; changing `store` reloads the chat with the new value.

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.maker.co/maker-greatstore/get-started/installation/react.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
