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

# Chat element

> Display a chat channel in your app

export const Platform = ({value, children}) => {
  const values = Array.isArray(value) ? value : [value];
  return <div className="pp-content" data-platforms={values.join(" ")}>
			{children}
		</div>;
};

export const PlatformSelect = ({children}) => {
  const platforms = [{
    value: "react",
    label: "React"
  }, {
    value: "vanillajs",
    label: "Vanilla JS"
  }, {
    value: "swift",
    label: "Swift"
  }];
  const authMethods = [{
    value: "token",
    label: "Token"
  }, {
    value: "oauth",
    label: "OAuth"
  }];
  const [selected, setSelected] = React.useState("react");
  const [selectedAuth, setSelectedAuth] = React.useState("token");
  const wrapperRef = React.useRef(null);
  React.useEffect(() => {
    const wrapper = wrapperRef.current;
    if (!wrapper) return;
    const updateToc = () => {
      const hiddenIds = new Set();
      const visibleIds = new Set();
      wrapper.querySelectorAll(".pp-content").forEach(el => {
        const plats = (el.getAttribute("data-platforms") || "").split(" ");
        const isVisible = plats.includes(selected);
        el.querySelectorAll("[id]").forEach(child => {
          if (isVisible) {
            visibleIds.add(child.id);
          } else {
            hiddenIds.add(child.id);
          }
        });
      });
      wrapper.querySelectorAll(".pp-content-auth").forEach(el => {
        const modes = (el.getAttribute("data-auth-modes") || "").split(" ");
        const isVisible = modes.includes(selectedAuth);
        el.querySelectorAll("[id]").forEach(child => {
          if (isVisible) {
            visibleIds.add(child.id);
          } else {
            hiddenIds.add(child.id);
          }
        });
      });
      document.querySelectorAll('a[href^="#"]').forEach(a => {
        const id = decodeURIComponent(a.getAttribute("href").slice(1));
        const li = a.closest("li");
        if (!li) return;
        if (hiddenIds.has(id) && !visibleIds.has(id)) {
          li.style.display = "none";
        } else if (visibleIds.has(id) || hiddenIds.has(id)) {
          li.style.display = "";
        }
      });
    };
    updateToc();
    const timer = setTimeout(updateToc, 200);
    return () => clearTimeout(timer);
  }, [selected, selectedAuth]);
  return <div className="pp-wrapper" ref={wrapperRef} data-selected-platform={selected} data-selected-auth={selectedAuth}>
			<div className="pp-bar">
				<div className="pp-group">
					<span className="pp-label">Auth</span>
					<div className="pp-buttons">
						{authMethods.map(p => <button key={p.value} onClick={() => setSelectedAuth(p.value)} className={`pp-button ${selectedAuth === p.value ? "pp-button-active" : ""}`} aria-pressed={selectedAuth === p.value}>
								{p.label}
							</button>)}
					</div>
					<select className="pp-select" value={selectedAuth} onChange={e => setSelectedAuth(e.target.value)}>
						{authMethods.map(p => <option key={p.value} value={p.value}>
								{p.label}
							</option>)}
					</select>
				</div>
				<div className="pp-group">
					<span className="pp-label">Platform</span>
					<div className="pp-buttons">
						{platforms.map(p => <button key={p.value} onClick={() => setSelected(p.value)} className={`pp-button ${selected === p.value ? "pp-button-active" : ""}`} aria-pressed={selected === p.value}>
								{p.label}
							</button>)}
					</div>
					<select className="pp-select" value={selected} onChange={e => setSelected(e.target.value)}>
						{platforms.map(p => <option key={p.value} value={p.value}>
								{p.label}
							</option>)}
					</select>
				</div>
			</div>
			{children}
		</div>;
};

<PlatformSelect>
  The chat element renders a real-time chat UI connected to a specific channel.

  ### Basic usage

  Pass a `channelId` to connect to a specific chat channel.

  <Platform value="react">
    ```tsx theme={null}
    import { useMemo } from "react";
    import {
    	ChatElement,
    	ChatSession,
    	Elements,
    } from "@whop/embedded-components-react-js";
    import { loadWhopElements } from "@whop/embedded-components-vanilla-js";
    import type { ChatElementOptions } from "@whop/embedded-components-vanilla-js/types";

    const elements = loadWhopElements();

    async function getToken() {
    	const response = await fetch("/api/token");
    	const data = await response.json();
    	return data.token;
    }

    export function ChatPage() {
    	const chatOptions: ChatElementOptions = useMemo(() => {
    		return {
    			channelId: "chat_XXXXXXXXXXXXXX",
    		};
    	}, []);

    	return (
    		<Elements elements={elements}>
    			<ChatSession token={getToken}>
    				<ChatElement
    					options={chatOptions}
    					style={{ height: "100dvh", width: "100%" }}
    				/>
    			</ChatSession>
    		</Elements>
    	);
    }
    ```
  </Platform>

  <Platform value="vanillajs">
    ```typescript theme={null}
    import { loadWhopElements } from "@whop/embedded-components-vanilla-js";

    async function getToken() {
    	const response = await fetch("/api/token");
    	const data = await response.json();
    	return data.token;
    }

    const whopElements = await loadWhopElements();

    const session = whopElements.createChatSession({
    	token: getToken,
    });

    const chatElement = session.createElement("chat-element", {
    	channelId: "chat_XXXXXXXXXXXXXX",
    });

    chatElement.mount("#chat-container");
    ```
  </Platform>

  <Platform value="swift">
    Display a chat channel with `WhopChatView`:

    ```swift theme={null}
    import SwiftUI
    import WhopElements

    struct ChatView: View {
        var body: some View {
            WhopChatView(
                channelId: "chat_XXXXXXXXXXXXXX",
                style: .imessage
            )
        }
    }
    ```
  </Platform>

  ### Deeplinking to messages

  To scroll to and highlight a specific message, pass `deeplinkToPostId` in your options. The view will automatically navigate to that message.

  <Platform value="react">
    ```tsx theme={null}
    const chatOptions: ChatElementOptions = useMemo(() => {
    	return {
    		channelId: "chat_XXXXXXXXXXXXXX",
    		deeplinkToPostId: "post_XXXXXXXXXXXXXX",
    	};
    }, []);

    <ChatElement options={chatOptions} />;
    ```
  </Platform>

  <Platform value="vanillajs">
    ```typescript theme={null}
    const chatElement = session.createElement("chat-element", {
    	channelId: "chat_XXXXXXXXXXXXXX",
    	deeplinkToPostId: "post_XXXXXXXXXXXXXX",
    });

    chatElement.mount("#chat-container");
    ```
  </Platform>

  <Platform value="swift">
    When `deeplinkToPostId` changes, the chat will automatically navigate to the new message.

    ```swift theme={null}
    struct ChatView: View {
        @State private var targetPostId: String? = nil

        var body: some View {
            WhopChatView(
                channelId: "chat_XXXXXXXXXXXXXX",
                deeplinkToPostId: targetPostId,
                style: .imessage
            )
        }
    }
    ```
  </Platform>

  ### Event handling

  Listen to user interactions like profile clicks, link clicks, and sent messages using the `onEvent` callback.

  <Platform value="react">
    | Event             | Detail                                               | Description                                           |
    | ----------------- | ---------------------------------------------------- | ----------------------------------------------------- |
    | `profileClick`    | `{ id: string }`                                     | Emitted when the user clicks on a profile             |
    | `linkClick`       | `{ url: string }`                                    | Emitted when the user clicks on a link                |
    | `messageSent`     | `{ id: string, content: string, channelId: string }` | Emitted when the user sends a message                 |
    | `experienceClick` | `{ id: string }`                                     | Emitted when the user clicks on an experience mention |

    ```tsx theme={null}
    import { useCallback, useMemo } from "react";
    import type {
    	ChatElementEvent,
    	ChatElementOptions,
    } from "@whop/embedded-components-vanilla-js/types";

    const handleChatEvent = useCallback((event: ChatElementEvent) => {
    	switch (event.type) {
    		case "profileClick":
    			console.log("Profile clicked:", event.detail.id);
    			break;

    		case "linkClick":
    			console.log("Link clicked:", event.detail.url);
    			break;

    		case "messageSent":
    			console.log("Message sent:", event.detail.id, event.detail.content, event.detail.channelId);
    			break;

    		case "experienceClick":
    			console.log("Experience clicked:", event.detail.id);
    			break;
    	}
    }, []);

    const chatOptions: ChatElementOptions = useMemo(() => {
    	return {
    		channelId: "chat_XXXXXXXXXXXXXX",
    		onEvent: handleChatEvent,
    	};
    }, [handleChatEvent]);

    <ChatElement options={chatOptions} />;
    ```
  </Platform>

  <Platform value="vanillajs">
    | Event             | Detail                                               | Description                                           |
    | ----------------- | ---------------------------------------------------- | ----------------------------------------------------- |
    | `profileClick`    | `{ id: string }`                                     | Emitted when the user clicks on a profile             |
    | `linkClick`       | `{ url: string }`                                    | Emitted when the user clicks on a link                |
    | `messageSent`     | `{ id: string, content: string, channelId: string }` | Emitted when the user sends a message                 |
    | `experienceClick` | `{ id: string }`                                     | Emitted when the user clicks on an experience mention |

    ```typescript theme={null}
    const chatElement = session.createElement("chat-element", {
    	channelId: "chat_XXXXXXXXXXXXXX",
    });

    chatElement.on("profileClick", (ev) => {
    	console.log("Profile clicked:", ev.detail.id);
    });

    chatElement.on("linkClick", (ev) => {
    	console.log("Link clicked:", ev.detail.url);
    });

    chatElement.on("messageSent", (ev) => {
    	console.log("Message sent:", ev.detail.id, ev.detail.content, ev.detail.channelId);
    });

    chatElement.on("experienceClick", (ev) => {
    	console.log("Experience clicked:", ev.detail.id);
    });

    chatElement.mount("#chat-container");
    ```
  </Platform>

  <Platform value="swift">
    | Event          | Detail                               | Description                             |
    | -------------- | ------------------------------------ | --------------------------------------- |
    | `.profileTap`  | `username: String`                   | Emitted when the user taps on a profile |
    | `.urlTap`      | `url: String`                        | Emitted when the user taps on a link    |
    | `.messageSent` | `content: String, messageId: String` | Emitted when the user sends a message   |

    ```swift theme={null}
    WhopChatView(
        channelId: "chat_XXXXXXXXXXXXXX",
        style: .imessage,
        onEvent: { event in
            switch event {
            case let .profileTap(username):
                print("Profile tapped: \(username)")
            case let .urlTap(url):
                print("URL tapped: \(url)")
            case let .messageSent(content, messageId):
                print("Message sent: \(messageId) - \(content)")
            }
        }
    )
    ```
  </Platform>
</PlatformSelect>
