Documentation

Install in Next.js (App Router)

Mount the chat widget from a Next.js server component using a small client-side wrapper.

There's no @appmint/chat package

The chat-client repo is private: true and exposes nothing on npm. The "server component" pattern below is not a feature of a published package — it's a one-file convenience used in the yugo reference repo (/Users/imzee/projects/yugo/src/components/AppmintChatServer.tsx) that reads env vars on the server and forwards them to a 'use client' wrapper. Copy it into your project; it is ~8 lines of code.

The widget itself runs only in the browser (it injects a <script> tag and calls window.AppmintChat.init). The "server component" wrapper exists purely to pull configuration out of process env on the server so secret-ish values (org id, config id) never appear in your client bundle.

When to use this pattern

Reach for the server wrapper when:

  • You're on the App Router and want to read process.env server-side.
  • You'd rather not duplicate NEXT_PUBLIC_* prefixes for org/config ids.

For everything else (Vite, Pages Router, CSR-only React apps) the client wrapper is fine on its own.

The two files

Two files: a 'use client' wrapper that does the script-tag injection (same as the React installation), and a thin server component that reads env vars and forwards them.

AppmintChat.tsx (client component)

This is the wrapper from the React installation page. Copy it verbatim into src/components/AppmintChat.tsx.

AppmintChatServer.tsx (server component)

import AppmintChat from './AppmintChat';

export default function AppmintChatServer() {
  const configId = process.env.chatConfigId;
  const appId = process.env.chatAppId;
  const orgId = process.env.NEXT_PUBLIC_ORG_ID;
  return <AppmintChat configId={configId} appId={appId} orgId={orgId} />;
}

That's the entire server component — no fetch, no preloading, no SSR rendering of the panel. It just reads env vars and renders the client wrapper. The browser still does the actual script-tag injection on mount.

Usage in a layout

Drop the server component into your root layout so the widget is available on every page.

// app/layout.tsx
import AppmintChatServer from '@/components/AppmintChatServer';

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

No 'use client' directive on the layout — the server wrapper is a true server component, and the inner AppmintChat carries its own 'use client' boundary.

Environment variables

Set these in .env.local (and your hosting provider's env config):

NEXT_PUBLIC_ORG_ID=your-org-id
chatConfigId=67169ccce05fcd6eb50e6dff
chatAppId=storefront

The yugo reference uses lowercase, non-NEXT_PUBLIC_ names for chatConfigId and chatAppId so they're only readable on the server. Pick any names you like — the only one that needs to be public is NEXT_PUBLIC_ORG_ID if you also want it accessible from other client components.

Authenticated visitors

The yugo wrapper does not pre-fetch a chat token, mint a session, or call AppEngine — it only forwards env vars. If you need to attach an authenticated visitor's identity, extend AppmintChat.tsx to accept user and token props, then forward them from AppmintChatServer.tsx:

import AppmintChat from './AppmintChat';
import { getCurrentVisitor, getChatToken } from '@/lib/auth';

export default async function AppmintChatServer() {
  const visitor = await getCurrentVisitor();
  const chatToken = visitor ? await getChatToken(visitor.email) : null;

  return (
    <AppmintChat
      orgId={process.env.NEXT_PUBLIC_ORG_ID}
      configId={process.env.chatConfigId}
      appId={process.env.chatAppId}
      user={visitor ?? undefined}
      token={chatToken ?? undefined}
    />
  );
}

getCurrentVisitor and getChatToken are your code — typically wrappers around AppEngine's /profile/customer/signin and a token-mint helper. There is no built-in restHelper.register(...) exposed by the chat bundle.

Streaming and Suspense

AppmintChatServer returns synchronously after reading env vars. There's nothing to suspend on. If your layout streams, no special handling is needed — the wrapper renders an empty <div> and then the client takes over once the script loads.

Verifying behaviour

If the bubble doesn't appear:

  1. Open DevTools → Network and look for https://web.appmint.space/chat-client/appmint-chat.js returning 200.
  2. In the console, check typeof window.AppmintChat === 'object' after the script loads.
  3. Confirm configId and appId are present (the wrapper returns null if either is missing).

The ground truth lives in chat-client/src/index.tsx (the script-tag entry) and yugo/src/components/AppmintChat.tsx (the wrapper this page mirrors).