Skip to main content
The CopilotPopup component provides a floating popup chat interface, typically positioned in the bottom-right corner of the screen. It’s perfect for adding AI assistance to any application without disrupting the main content.

Basic Usage

import { CopilotPopup } from "@copilotkit/react-core";

function App() {
  return (
    <div>
      <main>
        {/* Your app content */}
      </main>
      
      <CopilotPopup />
    </div>
  );
}

Props

CopilotPopup accepts all CopilotChat props plus:
defaultOpen
boolean
Whether the popup is open by default. Defaults to false.
width
number | string
Width of the popup. Can be a number (pixels) or CSS string. Defaults to 400 (400px).
height
number | string
Height of the popup. Can be a number (pixels) or CSS string. Defaults to 600 (600px).
clickOutsideToClose
boolean
Whether clicking outside the popup closes it. Defaults to true.
header
React.ReactElement
Custom header component to display at the top of the popup.
toggleButton
React.ReactElement
Custom toggle button component. If not provided, a default floating button is rendered.
All other CopilotChat props are supported:
  • agentId
  • threadId
  • labels
  • onError
  • welcomeScreen
  • messageView
  • suggestionView
  • And more…

Examples

Basic Popup

import { CopilotPopup } from "@copilotkit/react-core";

function App() {
  return (
    <div>
      <header>
        <h1>My Application</h1>
      </header>
      
      <main>
        <p>Your content here</p>
      </main>
      
      <CopilotPopup />
    </div>
  );
}

Custom Size

import { CopilotPopup } from "@copilotkit/react-core";

function CustomSizePopup() {
  return (
    <div>
      <main>Content</main>
      
      {/* Larger popup */}
      <CopilotPopup 
        width={500} 
        height={700} 
      />
    </div>
  );
}

function ResponsivePopup() {
  return (
    <div>
      <main>Content</main>
      
      {/* Responsive sizing */}
      <CopilotPopup 
        width="90vw" 
        height="80vh" 
      />
    </div>
  );
}

Open by Default

import { CopilotPopup } from "@copilotkit/react-core";

function DefaultOpenPopup() {
  return (
    <div>
      <main>Content</main>
      
      <CopilotPopup 
        defaultOpen={true}
      />
    </div>
  );
}

Disable Click Outside to Close

import { CopilotPopup } from "@copilotkit/react-core";

function PersistentPopup() {
  return (
    <div>
      <main>Content</main>
      
      <CopilotPopup 
        clickOutsideToClose={false}
      />
    </div>
  );
}

Custom Header

import { CopilotPopup } from "@copilotkit/react-core";

function CustomHeaderPopup() {
  return (
    <div>
      <main>Content</main>
      
      <CopilotPopup
        header={
          <div style={{ 
            padding: "16px", 
            borderBottom: "1px solid #eee",
            display: "flex",
            justifyContent: "space-between",
            alignItems: "center"
          }}>
            <div>
              <h3 style={{ margin: 0 }}>AI Assistant</h3>
              <p style={{ margin: 0, fontSize: "12px", color: "#666" }}>
                Online
              </p>
            </div>
            <img src="/avatar.png" alt="AI" width="32" height="32" />
          </div>
        }
      />
    </div>
  );
}

Custom Toggle Button

import { CopilotPopup } from "@copilotkit/react-core";
import { Bot } from "lucide-react";

function CustomTogglePopup() {
  return (
    <div>
      <main>Content</main>
      
      <CopilotPopup
        toggleButton={
          <button
            style={{
              position: "fixed",
              bottom: "24px",
              right: "24px",
              width: "56px",
              height: "56px",
              borderRadius: "50%",
              background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
              color: "white",
              border: "none",
              cursor: "pointer",
              boxShadow: "0 4px 16px rgba(0,0,0,0.2)",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              transition: "transform 0.2s"
            }}
            onMouseEnter={(e) => e.currentTarget.style.transform = "scale(1.1)"}
            onMouseLeave={(e) => e.currentTarget.style.transform = "scale(1)"}
          >
            <Bot size={28} />
          </button>
        }
      />
    </div>
  );
}

Mobile Optimized

import { CopilotPopup } from "@copilotkit/react-core";
import { useMediaQuery } from "@/hooks/useMediaQuery";

function MobileOptimizedPopup() {
  const isMobile = useMediaQuery("(max-width: 768px)");
  
  return (
    <div>
      <main>Content</main>
      
      <CopilotPopup
        width={isMobile ? "100vw" : 400}
        height={isMobile ? "100vh" : 600}
      />
    </div>
  );
}

With Agent Selection

import { CopilotPopup } from "@copilotkit/react-core";
import { useState } from "react";

function MultiAgentPopup() {
  const [agent, setAgent] = useState("default");
  
  return (
    <div>
      <main>
        <h1>Choose Your Assistant</h1>
        <button onClick={() => setAgent("default")}>General</button>
        <button onClick={() => setAgent("code")}>Code Helper</button>
        <button onClick={() => setAgent("research")}>Research</button>
      </main>
      
      <CopilotPopup
        agentId={agent}
        header={
          <div style={{ padding: "16px", borderBottom: "1px solid #eee" }}>
            <h3>
              {agent === "default" && "General Assistant"}
              {agent === "code" && "Code Helper"}
              {agent === "research" && "Research Assistant"}
            </h3>
          </div>
        }
      />
    </div>
  );
}

Custom Styling

import { CopilotPopup } from "@copilotkit/react-core";

function StyledPopup() {
  return (
    <div>
      <main>Content</main>
      
      <CopilotPopup
        className="custom-popup"
        messageView={{ className: "custom-message" }}
        width={420}
        height={650}
        header={
          <div className="popup-header">
            <div className="status-indicator" />
            <h3>AI Assistant</h3>
          </div>
        }
      />
    </div>
  );
}

// In your CSS:
// .custom-popup {
//   border-radius: 16px;
//   box-shadow: 0 8px 32px rgba(0,0,0,0.15);
// }
// .popup-header {
//   background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
//   color: white;
//   padding: 20px;
// }

With Error Handling

import { CopilotPopup } from "@copilotkit/react-core";
import { useState } from "react";

function PopupWithErrors() {
  const [error, setError] = useState<string | null>(null);
  
  return (
    <div>
      <main>Content</main>
      
      {error && (
        <div 
          style={{
            position: "fixed",
            top: "20px",
            right: "20px",
            background: "#f44336",
            color: "white",
            padding: "16px",
            borderRadius: "8px",
            zIndex: 10001
          }}
        >
          {error}
          <button onClick={() => setError(null)}>×</button>
        </div>
      )}
      
      <CopilotPopup
        onError={({ error }) => {
          setError(error.message);
          setTimeout(() => setError(null), 5000);
        }}
      />
    </div>
  );
}

Minimalist Design

import { CopilotPopup } from "@copilotkit/react-core";
import { MessageCircle } from "lucide-react";

function MinimalistPopup() {
  return (
    <div>
      <main>Content</main>
      
      <CopilotPopup
        width={380}
        height={580}
        welcomeScreen={false}
        toggleButton={
          <button
            style={{
              position: "fixed",
              bottom: "20px",
              right: "20px",
              width: "50px",
              height: "50px",
              borderRadius: "25px",
              background: "#000",
              color: "white",
              border: "none",
              cursor: "pointer",
              display: "flex",
              alignItems: "center",
              justifyContent: "center"
            }}
          >
            <MessageCircle size={22} />
          </button>
        }
      />
    </div>
  );
}

Animation Behavior

The popup includes smooth animations:
  • Scales in from the bottom-right when opened
  • Fades and scales out when closed
  • Backdrop fades in/out
  • Smooth transitions (250ms duration)

Backdrop

When open, the popup displays a backdrop that:
  • Slightly darkens the background
  • Can close the popup when clicked (if clickOutsideToClose is true)
  • Is optional and can be styled

Position

The popup is positioned:
  • Fixed to the bottom-right corner by default
  • Above all other content (high z-index)
  • Does not affect document flow
  • Maintains position during scrolling

Best Practices

Choose Appropriate Size

Select dimensions that work well for your content. Consider mobile devices where you might want full screen.

Provide Visual Affordance

Make the toggle button prominent and easily discoverable. Use clear iconography like a message or chat bubble.

Mobile Considerations

On mobile, consider using full viewport dimensions for better usability.

Accessible Toggle

Ensure the toggle button has proper ARIA labels and is keyboard accessible.

Keyboard Navigation

The popup supports keyboard interaction:
  • Press Escape to close the popup
  • Tab through interactive elements
  • Focus is trapped within the popup when open
  • Toggle button is keyboard accessible

Accessibility

Built-in accessibility features:
  • ARIA labels and roles
  • Focus management
  • Screen reader support
  • Keyboard navigation
  • Clear visual focus indicators

Z-Index

The popup uses a high z-index to appear above most content:
  • Toggle button: z-index: 9999
  • Backdrop: z-index: 10000
  • Popup content: z-index: 10001
Adjust these if you have conflicting z-index layers in your app.

Performance

The popup is optimized for:
  • Lazy rendering (content renders only when open)
  • Efficient animations using CSS transforms
  • Minimal re-renders
  • Small bundle size

Comparison with Other Components

FeatureCopilotPopupCopilotChatCopilotSidebar
PositioningBottom-right, floatingEmbeddedRight edge, full height
DimensionsConfigurableParent containerFull height, custom width
ToggleBuilt-in buttonNoneBuilt-in button
BackdropYesNoYes
Click OutsideConfigurableN/ACloses sidebar
Best forQuick accessFull-screenDesktop apps
MobileFull screen optionResponsiveFull width

Integration Examples

With Existing Chat Button

import { CopilotPopup } from "@copilotkit/react-core";
import { useState } from "react";

function IntegratedPopup() {
  const [isOpen, setIsOpen] = useState(false);
  
  return (
    <div>
      <header>
        <button onClick={() => setIsOpen(true)}>
          Need Help?
        </button>
      </header>
      
      <main>Content</main>
      
      <CopilotPopup defaultOpen={isOpen} />
    </div>
  );
}

With Product Tours

import { CopilotPopup } from "@copilotkit/react-core";
import { useEffect, useState } from "react";

function OnboardingPopup() {
  const [showChat, setShowChat] = useState(false);
  
  useEffect(() => {
    // Show chat after user completes onboarding
    const onboardingComplete = localStorage.getItem("onboarding_complete");
    if (onboardingComplete) {
      setShowChat(true);
    }
  }, []);
  
  if (!showChat) return null;
  
  return <CopilotPopup defaultOpen={true} />;
}

TypeScript

interface CopilotPopupProps extends CopilotChatProps {
  defaultOpen?: boolean;
  width?: number | string;
  height?: number | string;
  clickOutsideToClose?: boolean;
  header?: React.ReactElement;
  toggleButton?: React.ReactElement;
}